Deleted Added
sdiff udiff text old ( 174984 ) new ( 180777 )
full compact
1/*
2 * kbdmux.c
3 */
4
5/*-
6 * Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
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.
17 *
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.
29 *
30 * $Id: kbdmux.c,v 1.4 2005/07/14 17:38:35 max Exp $
31 * $FreeBSD: head/sys/dev/kbdmux/kbdmux.c 180777 2008-07-24 09:54:10Z ed $
32 */
33
34#include "opt_compat.h"
35#include "opt_kbd.h"
36
37#include <sys/param.h>
38#include <sys/bus.h>
39#include <sys/clist.h>
40#include <sys/conf.h>
41#include <sys/consio.h>
42#include <sys/fcntl.h>
43#include <sys/kbio.h>
44#include <sys/kernel.h>
45#include <sys/limits.h>
46#include <sys/lock.h>
47#include <sys/malloc.h>
48#include <sys/module.h>
49#include <sys/mutex.h>
50#include <sys/poll.h>
51#include <sys/proc.h>
52#include <sys/queue.h>
53#include <sys/selinfo.h>
54#include <sys/systm.h>
55#include <sys/taskqueue.h>
56#include <sys/uio.h>
57#include <dev/kbd/kbdreg.h>
58#include <dev/kbd/kbdtables.h>
59
60#define KEYBOARD_NAME "kbdmux"
61
62MALLOC_DECLARE(M_KBDMUX);
63MALLOC_DEFINE(M_KBDMUX, KEYBOARD_NAME, "Keyboard multiplexor");
64
65/*****************************************************************************
66 *****************************************************************************
67 ** Keyboard state
68 *****************************************************************************
69 *****************************************************************************/
70
71#define KBDMUX_Q_SIZE 512 /* input queue size */
72
73/*
74 * XXX
75 * For now rely on Giant mutex to protect our data structures.
76 * Just like the rest of keyboard drivers and syscons(4) do.
77 * Note that callout is initialized as not MP-safe to make sure
78 * Giant is held.
79 */
80
81#if 0 /* not yet */
82#define KBDMUX_LOCK_DECL_GLOBAL \
83 struct mtx ks_lock
84#define KBDMUX_LOCK_INIT(s) \
85 mtx_init(&(s)->ks_lock, "kbdmux", NULL, MTX_DEF|MTX_RECURSE)
86#define KBDMUX_LOCK_DESTROY(s) \
87 mtx_destroy(&(s)->ks_lock)
88#define KBDMUX_LOCK(s) \
89 mtx_lock(&(s)->ks_lock)
90#define KBDMUX_UNLOCK(s) \
91 mtx_unlock(&(s)->ks_lock)
92#define KBDMUX_LOCK_ASSERT(s, w) \
93 mtx_assert(&(s)->ks_lock, (w))
94#define KBDMUX_SLEEP(s, f, d, t) \
95 msleep(&(s)->f, &(s)->ks_lock, PCATCH | (PZERO + 1), (d), (t))
96#define KBDMUX_CALLOUT_INIT(s) \
97 callout_init_mtx(&(s)->ks_timo, &(s)->ks_lock, 0)
98#define KBDMUX_QUEUE_INTR(s) \
99 taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task)
100#else
101#define KBDMUX_LOCK_DECL_GLOBAL
102
103#define KBDMUX_LOCK_INIT(s)
104
105#define KBDMUX_LOCK_DESTROY(s)
106
107#define KBDMUX_LOCK(s)
108
109#define KBDMUX_UNLOCK(s)
110
111#define KBDMUX_LOCK_ASSERT(s, w)
112
113#define KBDMUX_SLEEP(s, f, d, t) \
114 tsleep(&(s)->f, PCATCH | (PZERO + 1), (d), (t))
115#define KBDMUX_CALLOUT_INIT(s) \
116 callout_init(&(s)->ks_timo, 0)
117#define KBDMUX_QUEUE_INTR(s) \
118 taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task)
119#endif /* not yet */
120
121/*
122 * kbdmux keyboard
123 */
124struct kbdmux_kbd
125{
126 keyboard_t *kbd; /* keyboard */
127 SLIST_ENTRY(kbdmux_kbd) next; /* link to next */
128};
129
130typedef struct kbdmux_kbd kbdmux_kbd_t;
131
132/*
133 * kbdmux state
134 */
135struct kbdmux_state
136{
137 struct clist ks_inq; /* input chars queue */
138 struct task ks_task; /* interrupt task */
139 struct callout ks_timo; /* timeout handler */
140#define TICKS (hz) /* rate */
141
142 int ks_flags; /* flags */
143#define COMPOSE (1 << 0) /* compose char flag */
144#define POLLING (1 << 1) /* polling */
145#define TASK (1 << 2) /* interrupt task queued */
146
147 int ks_mode; /* K_XLATE, K_RAW, K_CODE */
148 int ks_state; /* state */
149 int ks_accents; /* accent key index (> 0) */
150 u_int ks_composed_char; /* composed char code */
151 u_char ks_prefix; /* AT scan code prefix */
152
153 SLIST_HEAD(, kbdmux_kbd) ks_kbds; /* keyboards */
154
155 KBDMUX_LOCK_DECL_GLOBAL;
156};
157
158typedef struct kbdmux_state kbdmux_state_t;
159
160/*****************************************************************************
161 *****************************************************************************
162 ** Helper functions
163 *****************************************************************************
164 *****************************************************************************/
165
166static task_fn_t kbdmux_kbd_intr;
167static timeout_t kbdmux_kbd_intr_timo;
168static kbd_callback_func_t kbdmux_kbd_event;
169
170/*
171 * Interrupt handler task
172 */
173void
174kbdmux_kbd_intr(void *xkbd, int pending)
175{
176 keyboard_t *kbd = (keyboard_t *) xkbd;
177 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
178
179 kbdd_intr(kbd, NULL);
180
181 KBDMUX_LOCK(state);
182
183 state->ks_flags &= ~TASK;
184 wakeup(&state->ks_task);
185
186 KBDMUX_UNLOCK(state);
187}
188
189/*
190 * Schedule interrupt handler on timeout. Called with locked state.
191 */
192void
193kbdmux_kbd_intr_timo(void *xstate)
194{
195 kbdmux_state_t *state = (kbdmux_state_t *) xstate;
196
197 KBDMUX_LOCK_ASSERT(state, MA_OWNED);
198
199 if (callout_pending(&state->ks_timo))
200 return; /* callout was reset */
201
202 if (!callout_active(&state->ks_timo))
203 return; /* callout was stopped */
204
205 callout_deactivate(&state->ks_timo);
206
207 /* queue interrupt task if needed */
208 if (state->ks_inq.c_cc > 0 && !(state->ks_flags & TASK) &&
209 KBDMUX_QUEUE_INTR(state) == 0)
210 state->ks_flags |= TASK;
211
212 /* re-schedule timeout */
213 callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state);
214}
215
216/*
217 * Process event from one of our keyboards
218 */
219static int
220kbdmux_kbd_event(keyboard_t *kbd, int event, void *arg)
221{
222 kbdmux_state_t *state = (kbdmux_state_t *) arg;
223
224 switch (event) {
225 case KBDIO_KEYINPUT: {
226 int c;
227
228 KBDMUX_LOCK(state);
229
230 /*
231 * Read all chars from the keyboard
232 *
233 * Turns out that atkbd(4) check_char() method may return
234 * "true" while read_char() method returns NOKEY. If this
235 * happens we could stuck in the loop below. Avoid this
236 * by breaking out of the loop if read_char() method returns
237 * NOKEY.
238 */
239
240 while (kbdd_check_char(kbd)) {
241 c = kbdd_read_char(kbd, 0);
242 if (c == NOKEY)
243 break;
244 if (c == ERRKEY)
245 continue; /* XXX ring bell */
246 if (!KBD_IS_BUSY(kbd))
247 continue; /* not open - discard the input */
248
249 putc(c, &state->ks_inq);
250 }
251
252 /* queue interrupt task if needed */
253 if (state->ks_inq.c_cc > 0 && !(state->ks_flags & TASK) &&
254 KBDMUX_QUEUE_INTR(state) == 0)
255 state->ks_flags |= TASK;
256
257 KBDMUX_UNLOCK(state);
258 } break;
259
260 case KBDIO_UNLOADING: {
261 kbdmux_kbd_t *k;
262
263 KBDMUX_LOCK(state);
264
265 SLIST_FOREACH(k, &state->ks_kbds, next)
266 if (k->kbd == kbd)
267 break;
268
269 if (k != NULL) {
270 kbd_release(k->kbd, &k->kbd);
271 SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
272
273 k->kbd = NULL;
274
275 free(k, M_KBDMUX);
276 }
277
278 KBDMUX_UNLOCK(state);
279 } break;
280
281 default:
282 return (EINVAL);
283 /* NOT REACHED */
284 }
285
286 return (0);
287}
288
289/****************************************************************************
290 ****************************************************************************
291 ** Keyboard driver
292 ****************************************************************************
293 ****************************************************************************/
294
295static int kbdmux_configure(int flags);
296static kbd_probe_t kbdmux_probe;
297static kbd_init_t kbdmux_init;
298static kbd_term_t kbdmux_term;
299static kbd_intr_t kbdmux_intr;
300static kbd_test_if_t kbdmux_test_if;
301static kbd_enable_t kbdmux_enable;
302static kbd_disable_t kbdmux_disable;
303static kbd_read_t kbdmux_read;
304static kbd_check_t kbdmux_check;
305static kbd_read_char_t kbdmux_read_char;
306static kbd_check_char_t kbdmux_check_char;
307static kbd_ioctl_t kbdmux_ioctl;
308static kbd_lock_t kbdmux_lock;
309static void kbdmux_clear_state_locked(kbdmux_state_t *state);
310static kbd_clear_state_t kbdmux_clear_state;
311static kbd_get_state_t kbdmux_get_state;
312static kbd_set_state_t kbdmux_set_state;
313static kbd_poll_mode_t kbdmux_poll;
314
315static keyboard_switch_t kbdmuxsw = {
316 .probe = kbdmux_probe,
317 .init = kbdmux_init,
318 .term = kbdmux_term,
319 .intr = kbdmux_intr,
320 .test_if = kbdmux_test_if,
321 .enable = kbdmux_enable,
322 .disable = kbdmux_disable,
323 .read = kbdmux_read,
324 .check = kbdmux_check,
325 .read_char = kbdmux_read_char,
326 .check_char = kbdmux_check_char,
327 .ioctl = kbdmux_ioctl,
328 .lock = kbdmux_lock,
329 .clear_state = kbdmux_clear_state,
330 .get_state = kbdmux_get_state,
331 .set_state = kbdmux_set_state,
332 .get_fkeystr = genkbd_get_fkeystr,
333 .poll = kbdmux_poll,
334 .diag = genkbd_diag,
335};
336
337/*
338 * Return the number of found keyboards
339 */
340static int
341kbdmux_configure(int flags)
342{
343 return (1);
344}
345
346/*
347 * Detect a keyboard
348 */
349static int
350kbdmux_probe(int unit, void *arg, int flags)
351{
352 if (resource_disabled(KEYBOARD_NAME, unit))
353 return (ENXIO);
354
355 return (0);
356}
357
358/*
359 * Reset and initialize the keyboard (stolen from atkbd.c)
360 */
361static int
362kbdmux_init(int unit, keyboard_t **kbdp, void *arg, int flags)
363{
364 keyboard_t *kbd = NULL;
365 kbdmux_state_t *state = NULL;
366 keymap_t *keymap = NULL;
367 accentmap_t *accmap = NULL;
368 fkeytab_t *fkeymap = NULL;
369 int error, needfree, fkeymap_size, delay[2];
370
371 if (*kbdp == NULL) {
372 *kbdp = kbd = malloc(sizeof(*kbd), M_KBDMUX, M_NOWAIT | M_ZERO);
373 state = malloc(sizeof(*state), M_KBDMUX, M_NOWAIT | M_ZERO);
374 keymap = malloc(sizeof(key_map), M_KBDMUX, M_NOWAIT);
375 accmap = malloc(sizeof(accent_map), M_KBDMUX, M_NOWAIT);
376 fkeymap = malloc(sizeof(fkey_tab), M_KBDMUX, M_NOWAIT);
377 fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
378 needfree = 1;
379
380 if ((kbd == NULL) || (state == NULL) || (keymap == NULL) ||
381 (accmap == NULL) || (fkeymap == NULL)) {
382 error = ENOMEM;
383 goto bad;
384 }
385
386 KBDMUX_LOCK_INIT(state);
387 clist_alloc_cblocks(&state->ks_inq,
388 KBDMUX_Q_SIZE, KBDMUX_Q_SIZE / 2);
389 TASK_INIT(&state->ks_task, 0, kbdmux_kbd_intr, (void *) kbd);
390 KBDMUX_CALLOUT_INIT(state);
391 SLIST_INIT(&state->ks_kbds);
392 } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
393 return (0);
394 } else {
395 kbd = *kbdp;
396 state = (kbdmux_state_t *) kbd->kb_data;
397 keymap = kbd->kb_keymap;
398 accmap = kbd->kb_accentmap;
399 fkeymap = kbd->kb_fkeytab;
400 fkeymap_size = kbd->kb_fkeytab_size;
401 needfree = 0;
402 }
403
404 if (!KBD_IS_PROBED(kbd)) {
405 /* XXX assume 101/102 keys keyboard */
406 kbd_init_struct(kbd, KEYBOARD_NAME, KB_101, unit, flags, 0, 0);
407 bcopy(&key_map, keymap, sizeof(key_map));
408 bcopy(&accent_map, accmap, sizeof(accent_map));
409 bcopy(fkey_tab, fkeymap,
410 imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
411 kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
412 kbd->kb_data = (void *)state;
413
414 KBD_FOUND_DEVICE(kbd);
415 KBD_PROBE_DONE(kbd);
416
417 KBDMUX_LOCK(state);
418 kbdmux_clear_state_locked(state);
419 state->ks_mode = K_XLATE;
420 KBDMUX_UNLOCK(state);
421 }
422
423 if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
424 kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
425
426 kbdmux_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
427
428 delay[0] = kbd->kb_delay1;
429 delay[1] = kbd->kb_delay2;
430 kbdmux_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
431
432 KBD_INIT_DONE(kbd);
433 }
434
435 if (!KBD_IS_CONFIGURED(kbd)) {
436 if (kbd_register(kbd) < 0) {
437 error = ENXIO;
438 goto bad;
439 }
440
441 KBD_CONFIG_DONE(kbd);
442
443 KBDMUX_LOCK(state);
444 callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state);
445 KBDMUX_UNLOCK(state);
446 }
447
448 return (0);
449bad:
450 if (needfree) {
451 if (state != NULL) {
452 clist_free_cblocks(&state->ks_inq);
453 free(state, M_KBDMUX);
454 }
455 if (keymap != NULL)
456 free(keymap, M_KBDMUX);
457 if (accmap != NULL)
458 free(accmap, M_KBDMUX);
459 if (fkeymap != NULL)
460 free(fkeymap, M_KBDMUX);
461 if (kbd != NULL) {
462 free(kbd, M_KBDMUX);
463 *kbdp = NULL; /* insure ref doesn't leak to caller */
464 }
465 }
466
467 return (error);
468}
469
470/*
471 * Finish using this keyboard
472 */
473static int
474kbdmux_term(keyboard_t *kbd)
475{
476 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
477 kbdmux_kbd_t *k;
478
479 KBDMUX_LOCK(state);
480
481 /* kill callout */
482 callout_stop(&state->ks_timo);
483
484 /* wait for interrupt task */
485 while (state->ks_flags & TASK)
486 KBDMUX_SLEEP(state, ks_task, "kbdmuxc", 0);
487
488 /* release all keyboards from the mux */
489 while ((k = SLIST_FIRST(&state->ks_kbds)) != NULL) {
490 kbd_release(k->kbd, &k->kbd);
491 SLIST_REMOVE_HEAD(&state->ks_kbds, next);
492
493 k->kbd = NULL;
494
495 free(k, M_KBDMUX);
496 }
497
498 /* flush input queue */
499 ndflush(&state->ks_inq, state->ks_inq.c_cc);
500 clist_free_cblocks(&state->ks_inq);
501
502 KBDMUX_UNLOCK(state);
503
504 kbd_unregister(kbd);
505
506 KBDMUX_LOCK_DESTROY(state);
507 bzero(state, sizeof(*state));
508 free(state, M_KBDMUX);
509
510 free(kbd->kb_keymap, M_KBDMUX);
511 free(kbd->kb_accentmap, M_KBDMUX);
512 free(kbd->kb_fkeytab, M_KBDMUX);
513 free(kbd, M_KBDMUX);
514
515 return (0);
516}
517
518/*
519 * Keyboard interrupt routine
520 */
521static int
522kbdmux_intr(keyboard_t *kbd, void *arg)
523{
524 int c;
525
526 if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
527 /* let the callback function to process the input */
528 (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
529 kbd->kb_callback.kc_arg);
530 } else {
531 /* read and discard the input; no one is waiting for input */
532 do {
533 c = kbdmux_read_char(kbd, FALSE);
534 } while (c != NOKEY);
535 }
536
537 return (0);
538}
539
540/*
541 * Test the interface to the device
542 */
543static int
544kbdmux_test_if(keyboard_t *kbd)
545{
546 return (0);
547}
548
549/*
550 * Enable the access to the device; until this function is called,
551 * the client cannot read from the keyboard.
552 */
553static int
554kbdmux_enable(keyboard_t *kbd)
555{
556 KBD_ACTIVATE(kbd);
557 return (0);
558}
559
560/*
561 * Disallow the access to the device
562 */
563static int
564kbdmux_disable(keyboard_t *kbd)
565{
566 KBD_DEACTIVATE(kbd);
567 return (0);
568}
569
570/*
571 * Read one byte from the keyboard if it's allowed
572 */
573static int
574kbdmux_read(keyboard_t *kbd, int wait)
575{
576 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
577 int c;
578
579 KBDMUX_LOCK(state);
580 c = getc(&state->ks_inq);
581 KBDMUX_UNLOCK(state);
582
583 if (c != -1)
584 kbd->kb_count ++;
585
586 return (KBD_IS_ACTIVE(kbd)? c : -1);
587}
588
589/*
590 * Check if data is waiting
591 */
592static int
593kbdmux_check(keyboard_t *kbd)
594{
595 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
596 int ready;
597
598 if (!KBD_IS_ACTIVE(kbd))
599 return (FALSE);
600
601 KBDMUX_LOCK(state);
602 ready = (state->ks_inq.c_cc > 0)? TRUE : FALSE;
603 KBDMUX_UNLOCK(state);
604
605 return (ready);
606}
607
608/*
609 * Read char from the keyboard (stolen from atkbd.c)
610 */
611static u_int
612kbdmux_read_char(keyboard_t *kbd, int wait)
613{
614 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
615 u_int action;
616 int scancode, keycode;
617
618 KBDMUX_LOCK(state);
619
620next_code:
621
622 /* do we have a composed char to return? */
623 if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
624 action = state->ks_composed_char;
625 state->ks_composed_char = 0;
626 if (action > UCHAR_MAX) {
627 KBDMUX_UNLOCK(state);
628
629 return (ERRKEY);
630 }
631
632 KBDMUX_UNLOCK(state);
633
634 return (action);
635 }
636
637 /* see if there is something in the keyboard queue */
638 scancode = getc(&state->ks_inq);
639 if (scancode == -1) {
640 if (state->ks_flags & POLLING) {
641 kbdmux_kbd_t *k;
642
643 SLIST_FOREACH(k, &state->ks_kbds, next) {
644 while (kbdd_check_char(k->kbd)) {
645 scancode = kbdd_read_char(k->kbd, 0);
646 if (scancode == NOKEY)
647 break;
648 if (scancode == ERRKEY)
649 continue;
650 if (!KBD_IS_BUSY(k->kbd))
651 continue;
652
653 putc(scancode, &state->ks_inq);
654 }
655 }
656
657 if (state->ks_inq.c_cc > 0)
658 goto next_code;
659 }
660
661 KBDMUX_UNLOCK(state);
662 return (NOKEY);
663 }
664 /* XXX FIXME: check for -1 if wait == 1! */
665
666 kbd->kb_count ++;
667
668 /* return the byte as is for the K_RAW mode */
669 if (state->ks_mode == K_RAW) {
670 KBDMUX_UNLOCK(state);
671 return (scancode);
672 }
673
674 /* translate the scan code into a keycode */
675 keycode = scancode & 0x7F;
676 switch (state->ks_prefix) {
677 case 0x00: /* normal scancode */
678 switch(scancode) {
679 case 0xB8: /* left alt (compose key) released */
680 if (state->ks_flags & COMPOSE) {
681 state->ks_flags &= ~COMPOSE;
682 if (state->ks_composed_char > UCHAR_MAX)
683 state->ks_composed_char = 0;
684 }
685 break;
686 case 0x38: /* left alt (compose key) pressed */
687 if (!(state->ks_flags & COMPOSE)) {
688 state->ks_flags |= COMPOSE;
689 state->ks_composed_char = 0;
690 }
691 break;
692 case 0xE0:
693 case 0xE1:
694 state->ks_prefix = scancode;
695 goto next_code;
696 }
697 break;
698 case 0xE0: /* 0xE0 prefix */
699 state->ks_prefix = 0;
700 switch (keycode) {
701 case 0x1C: /* right enter key */
702 keycode = 0x59;
703 break;
704 case 0x1D: /* right ctrl key */
705 keycode = 0x5A;
706 break;
707 case 0x35: /* keypad divide key */
708 keycode = 0x5B;
709 break;
710 case 0x37: /* print scrn key */
711 keycode = 0x5C;
712 break;
713 case 0x38: /* right alt key (alt gr) */
714 keycode = 0x5D;
715 break;
716 case 0x46: /* ctrl-pause/break on AT 101 (see below) */
717 keycode = 0x68;
718 break;
719 case 0x47: /* grey home key */
720 keycode = 0x5E;
721 break;
722 case 0x48: /* grey up arrow key */
723 keycode = 0x5F;
724 break;
725 case 0x49: /* grey page up key */
726 keycode = 0x60;
727 break;
728 case 0x4B: /* grey left arrow key */
729 keycode = 0x61;
730 break;
731 case 0x4D: /* grey right arrow key */
732 keycode = 0x62;
733 break;
734 case 0x4F: /* grey end key */
735 keycode = 0x63;
736 break;
737 case 0x50: /* grey down arrow key */
738 keycode = 0x64;
739 break;
740 case 0x51: /* grey page down key */
741 keycode = 0x65;
742 break;
743 case 0x52: /* grey insert key */
744 keycode = 0x66;
745 break;
746 case 0x53: /* grey delete key */
747 keycode = 0x67;
748 break;
749 /* the following 3 are only used on the MS "Natural" keyboard */
750 case 0x5b: /* left Window key */
751 keycode = 0x69;
752 break;
753 case 0x5c: /* right Window key */
754 keycode = 0x6a;
755 break;
756 case 0x5d: /* menu key */
757 keycode = 0x6b;
758 break;
759 case 0x5e: /* power key */
760 keycode = 0x6d;
761 break;
762 case 0x5f: /* sleep key */
763 keycode = 0x6e;
764 break;
765 case 0x63: /* wake key */
766 keycode = 0x6f;
767 break;
768 case 0x64: /* [JP106USB] backslash, underscore */
769 keycode = 0x73;
770 break;
771 default: /* ignore everything else */
772 goto next_code;
773 }
774 break;
775 case 0xE1: /* 0xE1 prefix */
776 /*
777 * The pause/break key on the 101 keyboard produces:
778 * E1-1D-45 E1-9D-C5
779 * Ctrl-pause/break produces:
780 * E0-46 E0-C6 (See above.)
781 */
782 state->ks_prefix = 0;
783 if (keycode == 0x1D)
784 state->ks_prefix = 0x1D;
785 goto next_code;
786 /* NOT REACHED */
787 case 0x1D: /* pause / break */
788 state->ks_prefix = 0;
789 if (keycode != 0x45)
790 goto next_code;
791 keycode = 0x68;
792 break;
793 }
794
795 /* XXX assume 101/102 keys AT keyboard */
796 switch (keycode) {
797 case 0x5c: /* print screen */
798 if (state->ks_flags & ALTS)
799 keycode = 0x54; /* sysrq */
800 break;
801 case 0x68: /* pause/break */
802 if (state->ks_flags & CTLS)
803 keycode = 0x6c; /* break */
804 break;
805 }
806
807 /* return the key code in the K_CODE mode */
808 if (state->ks_mode == K_CODE) {
809 KBDMUX_UNLOCK(state);
810 return (keycode | (scancode & 0x80));
811 }
812
813 /* compose a character code */
814 if (state->ks_flags & COMPOSE) {
815 switch (keycode | (scancode & 0x80)) {
816 /* key pressed, process it */
817 case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
818 state->ks_composed_char *= 10;
819 state->ks_composed_char += keycode - 0x40;
820 if (state->ks_composed_char > UCHAR_MAX) {
821 KBDMUX_UNLOCK(state);
822 return (ERRKEY);
823 }
824 goto next_code;
825 case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
826 state->ks_composed_char *= 10;
827 state->ks_composed_char += keycode - 0x47;
828 if (state->ks_composed_char > UCHAR_MAX) {
829 KBDMUX_UNLOCK(state);
830 return (ERRKEY);
831 }
832 goto next_code;
833 case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
834 state->ks_composed_char *= 10;
835 state->ks_composed_char += keycode - 0x4E;
836 if (state->ks_composed_char > UCHAR_MAX) {
837 KBDMUX_UNLOCK(state);
838 return (ERRKEY);
839 }
840 goto next_code;
841 case 0x52: /* keypad 0 */
842 state->ks_composed_char *= 10;
843 if (state->ks_composed_char > UCHAR_MAX) {
844 KBDMUX_UNLOCK(state);
845 return (ERRKEY);
846 }
847 goto next_code;
848
849 /* key released, no interest here */
850 case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
851 case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
852 case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
853 case 0xD2: /* keypad 0 */
854 goto next_code;
855
856 case 0x38: /* left alt key */
857 break;
858
859 default:
860 if (state->ks_composed_char > 0) {
861 state->ks_flags &= ~COMPOSE;
862 state->ks_composed_char = 0;
863 KBDMUX_UNLOCK(state);
864 return (ERRKEY);
865 }
866 break;
867 }
868 }
869
870 /* keycode to key action */
871 action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
872 &state->ks_state, &state->ks_accents);
873 if (action == NOKEY)
874 goto next_code;
875
876 KBDMUX_UNLOCK(state);
877
878 return (action);
879}
880
881/*
882 * Check if char is waiting
883 */
884static int
885kbdmux_check_char(keyboard_t *kbd)
886{
887 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
888 int ready;
889
890 if (!KBD_IS_ACTIVE(kbd))
891 return (FALSE);
892
893 KBDMUX_LOCK(state);
894
895 if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char != 0))
896 ready = TRUE;
897 else
898 ready = (state->ks_inq.c_cc > 0)? TRUE : FALSE;
899
900 KBDMUX_UNLOCK(state);
901
902 return (ready);
903}
904
905/*
906 * Keyboard ioctl's
907 */
908static int
909kbdmux_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
910{
911 static int delays[] = {
912 250, 500, 750, 1000
913 };
914
915 static int rates[] = {
916 34, 38, 42, 46, 50, 55, 59, 63,
917 68, 76, 84, 92, 100, 110, 118, 126,
918 136, 152, 168, 184, 200, 220, 236, 252,
919 272, 304, 336, 368, 400, 440, 472, 504
920 };
921
922 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
923 kbdmux_kbd_t *k;
924 keyboard_info_t *ki;
925 int error = 0, mode;
926#ifdef COMPAT_FREEBSD6
927 int ival;
928#endif
929
930 if (state == NULL)
931 return (ENXIO);
932
933 switch (cmd) {
934 case KBADDKBD: /* add keyboard to the mux */
935 ki = (keyboard_info_t *) arg;
936
937 if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
938 strcmp(ki->kb_name, "*") == 0)
939 return (EINVAL); /* bad input */
940
941 KBDMUX_LOCK(state);
942
943 SLIST_FOREACH(k, &state->ks_kbds, next)
944 if (k->kbd->kb_unit == ki->kb_unit &&
945 strcmp(k->kbd->kb_name, ki->kb_name) == 0)
946 break;
947
948 if (k != NULL) {
949 KBDMUX_UNLOCK(state);
950
951 return (0); /* keyboard already in the mux */
952 }
953
954 k = malloc(sizeof(*k), M_KBDMUX, M_NOWAIT | M_ZERO);
955 if (k == NULL) {
956 KBDMUX_UNLOCK(state);
957
958 return (ENOMEM); /* out of memory */
959 }
960
961 k->kbd = kbd_get_keyboard(
962 kbd_allocate(
963 ki->kb_name,
964 ki->kb_unit,
965 (void *) &k->kbd,
966 kbdmux_kbd_event, (void *) state));
967 if (k->kbd == NULL) {
968 KBDMUX_UNLOCK(state);
969 free(k, M_KBDMUX);
970
971 return (EINVAL); /* bad keyboard */
972 }
973
974 kbdd_enable(k->kbd);
975 kbdd_clear_state(k->kbd);
976
977 /* set K_RAW mode on slave keyboard */
978 mode = K_RAW;
979 error = kbdd_ioctl(k->kbd, KDSKBMODE, (caddr_t)&mode);
980 if (error == 0) {
981 /* set lock keys state on slave keyboard */
982 mode = state->ks_state & LOCK_MASK;
983 error = kbdd_ioctl(k->kbd, KDSKBSTATE, (caddr_t)&mode);
984 }
985
986 if (error != 0) {
987 KBDMUX_UNLOCK(state);
988
989 kbd_release(k->kbd, &k->kbd);
990 k->kbd = NULL;
991
992 free(k, M_KBDMUX);
993
994 return (error); /* could not set mode */
995 }
996
997 SLIST_INSERT_HEAD(&state->ks_kbds, k, next);
998
999 KBDMUX_UNLOCK(state);
1000 break;
1001
1002 case KBRELKBD: /* release keyboard from the mux */
1003 ki = (keyboard_info_t *) arg;
1004
1005 if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
1006 strcmp(ki->kb_name, "*") == 0)
1007 return (EINVAL); /* bad input */
1008
1009 KBDMUX_LOCK(state);
1010
1011 SLIST_FOREACH(k, &state->ks_kbds, next)
1012 if (k->kbd->kb_unit == ki->kb_unit &&
1013 strcmp(k->kbd->kb_name, ki->kb_name) == 0)
1014 break;
1015
1016 if (k != NULL) {
1017 error = kbd_release(k->kbd, &k->kbd);
1018 if (error == 0) {
1019 SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
1020
1021 k->kbd = NULL;
1022
1023 free(k, M_KBDMUX);
1024 }
1025 } else
1026 error = ENXIO; /* keyboard is not in the mux */
1027
1028 KBDMUX_UNLOCK(state);
1029 break;
1030
1031 case KDGKBMODE: /* get kyboard mode */
1032 KBDMUX_LOCK(state);
1033 *(int *)arg = state->ks_mode;
1034 KBDMUX_UNLOCK(state);
1035 break;
1036
1037#ifdef COMPAT_FREEBSD6
1038 case _IO('K', 7):
1039 ival = IOCPARM_IVAL(arg);
1040 arg = (caddr_t)&ival;
1041 /* FALLTHROUGH */
1042#endif
1043 case KDSKBMODE: /* set keyboard mode */
1044 KBDMUX_LOCK(state);
1045
1046 switch (*(int *)arg) {
1047 case K_XLATE:
1048 if (state->ks_mode != K_XLATE) {
1049 /* make lock key state and LED state match */
1050 state->ks_state &= ~LOCK_MASK;
1051 state->ks_state |= KBD_LED_VAL(kbd);
1052 }
1053 /* FALLTHROUGH */
1054
1055 case K_RAW:
1056 case K_CODE:
1057 if (state->ks_mode != *(int *)arg) {
1058 kbdmux_clear_state_locked(state);
1059 state->ks_mode = *(int *)arg;
1060 }
1061 break;
1062
1063 default:
1064 error = EINVAL;
1065 break;
1066 }
1067
1068 KBDMUX_UNLOCK(state);
1069 break;
1070
1071 case KDGETLED: /* get keyboard LED */
1072 KBDMUX_LOCK(state);
1073 *(int *)arg = KBD_LED_VAL(kbd);
1074 KBDMUX_UNLOCK(state);
1075 break;
1076
1077#ifdef COMPAT_FREEBSD6
1078 case _IO('K', 66):
1079 ival = IOCPARM_IVAL(arg);
1080 arg = (caddr_t)&ival;
1081 /* FALLTHROUGH */
1082#endif
1083 case KDSETLED: /* set keyboard LED */
1084 KBDMUX_LOCK(state);
1085
1086 /* NOTE: lock key state in ks_state won't be changed */
1087 if (*(int *)arg & ~LOCK_MASK) {
1088 KBDMUX_UNLOCK(state);
1089
1090 return (EINVAL);
1091 }
1092
1093 KBD_LED_VAL(kbd) = *(int *)arg;
1094
1095 /* KDSETLED on all slave keyboards */
1096 SLIST_FOREACH(k, &state->ks_kbds, next)
1097 kbdd_ioctl(k->kbd, KDSETLED, arg);
1098
1099 KBDMUX_UNLOCK(state);
1100 break;
1101
1102 case KDGKBSTATE: /* get lock key state */
1103 KBDMUX_LOCK(state);
1104 *(int *)arg = state->ks_state & LOCK_MASK;
1105 KBDMUX_UNLOCK(state);
1106 break;
1107
1108#ifdef COMPAT_FREEBSD6
1109 case _IO('K', 20):
1110 ival = IOCPARM_IVAL(arg);
1111 arg = (caddr_t)&ival;
1112 /* FALLTHROUGH */
1113#endif
1114 case KDSKBSTATE: /* set lock key state */
1115 KBDMUX_LOCK(state);
1116
1117 if (*(int *)arg & ~LOCK_MASK) {
1118 KBDMUX_UNLOCK(state);
1119
1120 return (EINVAL);
1121 }
1122
1123 state->ks_state &= ~LOCK_MASK;
1124 state->ks_state |= *(int *)arg;
1125
1126 /* KDSKBSTATE on all slave keyboards */
1127 SLIST_FOREACH(k, &state->ks_kbds, next)
1128 kbdd_ioctl(k->kbd, KDSKBSTATE, arg);
1129
1130 KBDMUX_UNLOCK(state);
1131
1132 return (kbdmux_ioctl(kbd, KDSETLED, arg));
1133 /* NOT REACHED */
1134
1135#ifdef COMPAT_FREEBSD6
1136 case _IO('K', 67):
1137 cmd = KDSETRAD;
1138 ival = IOCPARM_IVAL(arg);
1139 arg = (caddr_t)&ival;
1140 /* FALLTHROUGH */
1141#endif
1142 case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
1143 case KDSETRAD: /* set keyboard repeat rate (old interface) */
1144 KBDMUX_LOCK(state);
1145
1146 if (cmd == KDSETREPEAT) {
1147 int i;
1148
1149 /* lookup delay */
1150 for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; i --)
1151 if (((int *)arg)[0] >= delays[i])
1152 break;
1153 mode = i << 5;
1154
1155 /* lookup rate */
1156 for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; i --)
1157 if (((int *)arg)[1] >= rates[i])
1158 break;
1159 mode |= i;
1160 } else
1161 mode = *(int *)arg;
1162
1163 if (mode & ~0x7f) {
1164 KBDMUX_UNLOCK(state);
1165
1166 return (EINVAL);
1167 }
1168
1169 kbd->kb_delay1 = delays[(mode >> 5) & 3];
1170 kbd->kb_delay2 = rates[mode & 0x1f];
1171
1172 /* perform command on all slave keyboards */
1173 SLIST_FOREACH(k, &state->ks_kbds, next)
1174 kbdd_ioctl(k->kbd, cmd, arg);
1175
1176 KBDMUX_UNLOCK(state);
1177 break;
1178
1179 case PIO_KEYMAP: /* set keyboard translation table */
1180 case PIO_KEYMAPENT: /* set keyboard translation table entry */
1181 case PIO_DEADKEYMAP: /* set accent key translation table */
1182 KBDMUX_LOCK(state);
1183 state->ks_accents = 0;
1184
1185 /* perform command on all slave keyboards */
1186 SLIST_FOREACH(k, &state->ks_kbds, next)
1187 kbdd_ioctl(k->kbd, cmd, arg);
1188
1189 KBDMUX_UNLOCK(state);
1190 /* FALLTHROUGH */
1191
1192 default:
1193 error = genkbd_commonioctl(kbd, cmd, arg);
1194 break;
1195 }
1196
1197 return (error);
1198}
1199
1200/*
1201 * Lock the access to the keyboard
1202 */
1203static int
1204kbdmux_lock(keyboard_t *kbd, int lock)
1205{
1206 return (1); /* XXX */
1207}
1208
1209/*
1210 * Clear the internal state of the keyboard
1211 */
1212static void
1213kbdmux_clear_state_locked(kbdmux_state_t *state)
1214{
1215 KBDMUX_LOCK_ASSERT(state, MA_OWNED);
1216
1217 state->ks_flags &= ~(COMPOSE|POLLING);
1218 state->ks_state &= LOCK_MASK; /* preserve locking key state */
1219 state->ks_accents = 0;
1220 state->ks_composed_char = 0;
1221/* state->ks_prefix = 0; XXX */
1222
1223 ndflush(&state->ks_inq, state->ks_inq.c_cc);
1224}
1225
1226static void
1227kbdmux_clear_state(keyboard_t *kbd)
1228{
1229 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
1230
1231 KBDMUX_LOCK(state);
1232 kbdmux_clear_state_locked(state);
1233 KBDMUX_UNLOCK(state);
1234}
1235
1236/*
1237 * Save the internal state
1238 */
1239static int
1240kbdmux_get_state(keyboard_t *kbd, void *buf, size_t len)
1241{
1242 if (len == 0)
1243 return (sizeof(kbdmux_state_t));
1244 if (len < sizeof(kbdmux_state_t))
1245 return (-1);
1246
1247 bcopy(kbd->kb_data, buf, sizeof(kbdmux_state_t)); /* XXX locking? */
1248
1249 return (0);
1250}
1251
1252/*
1253 * Set the internal state
1254 */
1255static int
1256kbdmux_set_state(keyboard_t *kbd, void *buf, size_t len)
1257{
1258 if (len < sizeof(kbdmux_state_t))
1259 return (ENOMEM);
1260
1261 bcopy(buf, kbd->kb_data, sizeof(kbdmux_state_t)); /* XXX locking? */
1262
1263 return (0);
1264}
1265
1266/*
1267 * Set polling
1268 */
1269static int
1270kbdmux_poll(keyboard_t *kbd, int on)
1271{
1272 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
1273 kbdmux_kbd_t *k;
1274
1275 KBDMUX_LOCK(state);
1276
1277 if (on)
1278 state->ks_flags |= POLLING;
1279 else
1280 state->ks_flags &= ~POLLING;
1281
1282 /* set poll on slave keyboards */
1283 SLIST_FOREACH(k, &state->ks_kbds, next)
1284 kbdd_poll(k->kbd, on);
1285
1286 KBDMUX_UNLOCK(state);
1287
1288 return (0);
1289}
1290
1291/*****************************************************************************
1292 *****************************************************************************
1293 ** Module
1294 *****************************************************************************
1295 *****************************************************************************/
1296
1297KEYBOARD_DRIVER(kbdmux, kbdmuxsw, kbdmux_configure);
1298
1299static int
1300kbdmux_modevent(module_t mod, int type, void *data)
1301{
1302 keyboard_switch_t *sw;
1303 keyboard_t *kbd;
1304 int error;
1305
1306 switch (type) {
1307 case MOD_LOAD:
1308 if ((error = kbd_add_driver(&kbdmux_kbd_driver)) != 0)
1309 break;
1310
1311 if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL) {
1312 kbd_delete_driver(&kbdmux_kbd_driver);
1313 error = ENXIO;
1314 break;
1315 }
1316
1317 kbd = NULL;
1318
1319 if ((error = (*sw->probe)(0, NULL, 0)) != 0 ||
1320 (error = (*sw->init)(0, &kbd, NULL, 0)) != 0) {
1321 kbd_delete_driver(&kbdmux_kbd_driver);
1322 break;
1323 }
1324
1325#ifdef KBD_INSTALL_CDEV
1326 if ((error = kbd_attach(kbd)) != 0) {
1327 (*sw->term)(kbd);
1328 kbd_delete_driver(&kbdmux_kbd_driver);
1329 break;
1330 }
1331#endif
1332
1333 if ((error = (*sw->enable)(kbd)) != 0) {
1334 (*sw->disable)(kbd);
1335#ifdef KBD_INSTALL_CDEV
1336 kbd_detach(kbd);
1337#endif
1338 (*sw->term)(kbd);
1339 kbd_delete_driver(&kbdmux_kbd_driver);
1340 break;
1341 }
1342 break;
1343
1344 case MOD_UNLOAD:
1345 if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL)
1346 panic("kbd_get_switch(" KEYBOARD_NAME ") == NULL");
1347
1348 kbd = kbd_get_keyboard(kbd_find_keyboard(KEYBOARD_NAME, 0));
1349 if (kbd == NULL)
1350 panic("kbd_get_keyboard(kbd_find_keyboard(" KEYBOARD_NAME ", 0)) == NULL");
1351
1352 (*sw->disable)(kbd);
1353#ifdef KBD_INSTALL_CDEV
1354 kbd_detach(kbd);
1355#endif
1356 (*sw->term)(kbd);
1357 kbd_delete_driver(&kbdmux_kbd_driver);
1358 error = 0;
1359 break;
1360
1361 default:
1362 error = EOPNOTSUPP;
1363 break;
1364 }
1365
1366 return (0);
1367}
1368
1369DEV_MODULE(kbdmux, kbdmux_modevent, NULL);
1370