Deleted Added
full compact
1/*-
2 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer as
10 * the first lines of this file unmodified.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/kbd/kbd.c 51658 1999-09-25 18:24:47Z phk $
26 * $FreeBSD: head/sys/dev/kbd/kbd.c 54382 1999-12-10 04:31:33Z yokota $
27 */
28
29#include "kbd.h"
30#include "opt_kbd.h"
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/malloc.h>
36#include <sys/conf.h>
37#include <sys/proc.h>
38#include <sys/tty.h>
39#include <sys/poll.h>
40#include <sys/vnode.h>
41#include <sys/uio.h>
42
43#include <machine/console.h>
44
45#include <dev/kbd/kbdreg.h>
46
47#define KBD_INDEX(dev) minor(dev)
48
49typedef struct genkbd_softc {
50 int gkb_flags; /* flag/status bits */
51#define KB_ASLEEP (1 << 0)
52 struct clist gkb_q; /* input queue */
53 struct selinfo gkb_rsel;
54} genkbd_softc_t;
55
56/* local arrays */
57
58/*
59 * We need at least one entry each in order to initialize a keyboard
60 * for the kernel console. The arrays will be increased dynamically
61 * when necessary.
62 */
63
64static int keyboards = 1;
65static keyboard_t *kbd_ini;
66static keyboard_t **keyboard = &kbd_ini;
67static keyboard_switch_t *kbdsw_ini;
68 keyboard_switch_t **kbdsw = &kbdsw_ini;
69
70#define ARRAY_DELTA 4
71
72static int
73kbd_realloc_array(void)
74{
75 keyboard_t **new_kbd;
76 keyboard_switch_t **new_kbdsw;
77 int newsize;
78 int s;
79
80 s = spltty();
81 newsize = ((keyboards + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA;
82 new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT);
83 if (new_kbd == NULL) {
84 splx(s);
85 return ENOMEM;
86 }
87 new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF, M_NOWAIT);
88 if (new_kbdsw == NULL) {
89 free(new_kbd, M_DEVBUF);
90 splx(s);
91 return ENOMEM;
92 }
93 bzero(new_kbd, sizeof(*new_kbd)*newsize);
94 bzero(new_kbdsw, sizeof(*new_kbdsw)*newsize);
95 bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards);
96 bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards);
97 if (keyboards > 1) {
98 free(keyboard, M_DEVBUF);
99 free(kbdsw, M_DEVBUF);
100 }
101 keyboard = new_kbd;
102 kbdsw = new_kbdsw;
103 keyboards = newsize;
104 splx(s);
105
106 if (bootverbose)
107 printf("kbd: new array size %d\n", keyboards);
108
109 return 0;
110}
111
112/*
113 * Low-level keyboard driver functions
114 * Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard
115 * driver, call these functions to initialize the keyboard_t structure
116 * and register it to the virtual keyboard driver `kbd'.
117 */
118
119/* initialize the keyboard_t structure */
120void
121kbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config,
122 int port, int port_size)
123{
124 kbd->kb_flags = KB_NO_DEVICE; /* device has not been found */
125 kbd->kb_name = name;
126 kbd->kb_type = type;
127 kbd->kb_unit = unit;
128 kbd->kb_config = config & ~KB_CONF_PROBE_ONLY;
129 kbd->kb_led = 0; /* unknown */
130 kbd->kb_io_base = port;
131 kbd->kb_io_size = port_size;
132 kbd->kb_data = NULL;
133 kbd->kb_keymap = NULL;
134 kbd->kb_accentmap = NULL;
135 kbd->kb_fkeytab = NULL;
136 kbd->kb_fkeytab_size = 0;
137 kbd->kb_delay1 = KB_DELAY1; /* these values are advisory only */
138 kbd->kb_delay2 = KB_DELAY2;
139 kbd->kb_prev_key = 0;
140 kbd->kb_count = 0L;
141}
142
143void
144kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
145 fkeytab_t *fkeymap, int fkeymap_size)
146{
147 kbd->kb_keymap = keymap;
148 kbd->kb_accentmap = accmap;
149 kbd->kb_fkeytab = fkeymap;
150 kbd->kb_fkeytab_size = fkeymap_size;
151}
152
153/* register a keyboard and associate it with a function table */
154int
155kbd_register(keyboard_t *kbd)
156{
157 const keyboard_driver_t **list;
158 const keyboard_driver_t *p;
159 int index;
160
161 for (index = 0; index < keyboards; ++index) {
162 if (keyboard[index] == NULL)
163 break;
164 }
165 if (index >= keyboards) {
166 if (kbd_realloc_array())
167 return -1;
168 }
169
170 kbd->kb_index = index;
171 KBD_UNBUSY(kbd);
172 KBD_VALID(kbd);
173 kbd->kb_active = 0; /* disabled until someone calls kbd_enable() */
174 kbd->kb_token = NULL;
175 kbd->kb_callback.kc_func = NULL;
176 kbd->kb_callback.kc_arg = NULL;
177
178 list = (const keyboard_driver_t **)kbddriver_set.ls_items;
179 while ((p = *list++) != NULL) {
180 if (strcmp(p->name, kbd->kb_name) == 0) {
181 keyboard[index] = kbd;
182 kbdsw[index] = p->kbdsw;
183 return index;
184 }
185 }
186
187 return -1;
188}
189
190int
191kbd_unregister(keyboard_t *kbd)
192{
193 int error;
194 int s;
195
196 if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards))
197 return ENOENT;
198 if (keyboard[kbd->kb_index] != kbd)
199 return ENOENT;
200
201 s = spltty();
202 if (KBD_IS_BUSY(kbd)) {
203 error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING,
204 kbd->kb_callback.kc_arg);
205 if (error) {
206 splx(s);
207 return error;
208 }
209 if (KBD_IS_BUSY(kbd)) {
210 splx(s);
211 return EBUSY;
212 }
213 }
214 KBD_INVALID(kbd);
215 keyboard[kbd->kb_index] = NULL;
216 kbdsw[kbd->kb_index] = NULL;
217
218 splx(s);
219 return 0;
220}
221
222/* find a funciton table by the driver name */
223keyboard_switch_t
224*kbd_get_switch(char *driver)
225{
226 const keyboard_driver_t **list;
227 const keyboard_driver_t *p;
228
229 list = (const keyboard_driver_t **)kbddriver_set.ls_items;
230 while ((p = *list++) != NULL) {
231 if (strcmp(p->name, driver) == 0)
232 return p->kbdsw;
233 }
234
235 return NULL;
236}
237
238/*
239 * Keyboard client functions
240 * Keyboard clients, such as the console driver `syscons' and the keyboard
241 * cdev driver, use these functions to claim and release a keyboard for
242 * exclusive use.
243 */
244
245/* find the keyboard specified by a driver name and a unit number */
246int
247kbd_find_keyboard(char *driver, int unit)
248{
249 int i;
250
251 for (i = 0; i < keyboards; ++i) {
252 if (keyboard[i] == NULL)
253 continue;
254 if (!KBD_IS_VALID(keyboard[i]))
255 continue;
256 if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver))
257 continue;
258 if ((unit != -1) && (keyboard[i]->kb_unit != unit))
259 continue;
260 return i;
261 }
262 return -1;
263}
264
265/* allocate a keyboard */
266int
267kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func,
268 void *arg)
269{
270 int index;
271 int s;
272
273 if (func == NULL)
274 return -1;
275
276 s = spltty();
277 index = kbd_find_keyboard(driver, unit);
278 if (index >= 0) {
279 if (KBD_IS_BUSY(keyboard[index])) {
280 splx(s);
281 return -1;
282 }
283 keyboard[index]->kb_token = id;
284 KBD_BUSY(keyboard[index]);
285 keyboard[index]->kb_callback.kc_func = func;
286 keyboard[index]->kb_callback.kc_arg = arg;
287 (*kbdsw[index]->clear_state)(keyboard[index]);
288 }
289 splx(s);
290 return index;
291}
292
293int
294kbd_release(keyboard_t *kbd, void *id)
295{
296 int error;
297 int s;
298
299 s = spltty();
300 if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
301 error = EINVAL;
302 } else if (kbd->kb_token != id) {
303 error = EPERM;
304 } else {
305 kbd->kb_token = NULL;
306 KBD_UNBUSY(kbd);
307 kbd->kb_callback.kc_func = NULL;
308 kbd->kb_callback.kc_arg = NULL;
309 (*kbdsw[kbd->kb_index]->clear_state)(kbd);
310 error = 0;
311 }
312 splx(s);
313 return error;
314}
315
316int
317kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func,
318 void *arg)
319{
320 int error;
321 int s;
322
323 s = spltty();
324 if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
325 error = EINVAL;
326 } else if (kbd->kb_token != id) {
327 error = EPERM;
328 } else if (func == NULL) {
329 error = EINVAL;
330 } else {
331 kbd->kb_callback.kc_func = func;
332 kbd->kb_callback.kc_arg = arg;
333 error = 0;
334 }
335 splx(s);
336 return error;
337}
338
339/* get a keyboard structure */
340keyboard_t
341*kbd_get_keyboard(int index)
342{
343 if ((index < 0) || (index >= keyboards))
344 return NULL;
345 if (keyboard[index] == NULL)
346 return NULL;
347 if (!KBD_IS_VALID(keyboard[index]))
348 return NULL;
349 return keyboard[index];
350}
351
352/*
353 * The back door for the console driver; configure keyboards
354 * This function is for the kernel console to initialize keyboards
355 * at very early stage.
356 */
357
358int
359kbd_configure(int flags)
360{
361 const keyboard_driver_t **list;
362 const keyboard_driver_t *p;
363
364 list = (const keyboard_driver_t **)kbddriver_set.ls_items;
365 while ((p = *list++) != NULL) {
366 if (p->configure != NULL)
367 (*p->configure)(flags);
368 }
369
370 return 0;
371}
372
373#ifdef KBD_INSTALL_CDEV
374
375/*
376 * Virtual keyboard cdev driver functions
377 * The virtual keyboard driver dispatches driver functions to
378 * appropriate subdrivers.
379 */
380
381#define KBD_UNIT(dev) minor(dev)
382
383static d_open_t genkbdopen;
384static d_close_t genkbdclose;
385static d_read_t genkbdread;
386static d_write_t genkbdwrite;
387static d_ioctl_t genkbdioctl;
388static d_poll_t genkbdpoll;
389
390#define CDEV_MAJOR 112
391
392static struct cdevsw kbd_cdevsw = {
393 /* open */ genkbdopen,
394 /* close */ genkbdclose,
395 /* read */ genkbdread,
396 /* write */ genkbdwrite,
397 /* ioctl */ genkbdioctl,
398 /* poll */ genkbdpoll,
399 /* mmap */ nommap,
400 /* strategy */ nostrategy,
401 /* name */ "kbd",
402 /* maj */ CDEV_MAJOR,
403 /* dump */ nodump,
404 /* psize */ nopsize,
405 /* flags */ 0,
406 /* bmaj */ -1
407};
408
409int
410kbd_attach(keyboard_t *kbd)
411{
412 dev_t dev;
413
414 if (kbd->kb_index >= keyboards)
415 return EINVAL;
416 if (keyboard[kbd->kb_index] != kbd)
417 return EINVAL;
418
419 dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL, 0600,
420 "kbd%r", kbd->kb_index);
421 if (dev->si_drv1 == NULL)
422 dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF,
423 M_WAITOK);
424 bzero(dev->si_drv1, sizeof(genkbd_softc_t));
425
426 printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit);
427 return 0;
428}
429
430int
431kbd_detach(keyboard_t *kbd)
432{
433 if (kbd->kb_index >= keyboards)
434 return EINVAL;
435 if (keyboard[kbd->kb_index] != kbd)
436 return EINVAL;
437
438 /* XXX: unmake_dev() ? */
439 return 0;
440}
441
442/*
443 * Generic keyboard cdev driver functions
444 * Keyboard subdrivers may call these functions to implement common
445 * driver functions.
446 */
447
448#define KB_QSIZE 512
449#define KB_BUFSIZE 64
450
451static kbd_callback_func_t genkbd_event;
452
453static int
454genkbdopen(dev_t dev, int mode, int flag, struct proc *p)
455{
456 keyboard_t *kbd;
457 genkbd_softc_t *sc;
458 int s;
459 int i;
460
461 s = spltty();
462 sc = dev->si_drv1;
463 kbd = kbd_get_keyboard(KBD_INDEX(dev));
464 if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
465 splx(s);
466 return ENXIO;
467 }
468 i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc,
469 genkbd_event, (void *)sc);
470 if (i < 0) {
471 splx(s);
472 return EBUSY;
473 }
474 /* assert(i == kbd->kb_index) */
475 /* assert(kbd == kbd_get_keyboard(i)) */
476
477 /*
478 * NOTE: even when we have successfully claimed a keyboard,
479 * the device may still be missing (!KBD_HAS_DEVICE(kbd)).
480 */
481
482#if 0
483 bzero(&sc->gkb_q, sizeof(sc->gkb_q));
484#endif
485 clist_alloc_cblocks(&sc->gkb_q, KB_QSIZE, KB_QSIZE/2); /* XXX */
486 sc->gkb_rsel.si_flags = 0;
487 sc->gkb_rsel.si_pid = 0;
488 splx(s);
489
490 return 0;
491}
492
493static int
494genkbdclose(dev_t dev, int mode, int flag, struct proc *p)
495{
496 keyboard_t *kbd;
497 genkbd_softc_t *sc;
498 int s;
499
500 /*
501 * NOTE: the device may have already become invalid.
502 * kbd == NULL || !KBD_IS_VALID(kbd)
503 */
504 s = spltty();
505 sc = dev->si_drv1;
506 kbd = kbd_get_keyboard(KBD_INDEX(dev));
507 if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
508 /* XXX: we shall be forgiving and don't report error... */
509 } else {
510 kbd_release(kbd, (void *)sc);
511#if 0
512 clist_free_cblocks(&sc->gkb_q);
513#endif
514 }
515 splx(s);
516 return 0;
517}
518
519static int
520genkbdread(dev_t dev, struct uio *uio, int flag)
521{
522 keyboard_t *kbd;
523 genkbd_softc_t *sc;
524 u_char buffer[KB_BUFSIZE];
525 int len;
526 int error;
527 int s;
528
529 /* wait for input */
530 s = spltty();
531 sc = dev->si_drv1;
532 kbd = kbd_get_keyboard(KBD_INDEX(dev));
533 if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
534 splx(s);
535 return ENXIO;
536 }
537 while (sc->gkb_q.c_cc == 0) {
538 if (flag & IO_NDELAY) {
539 splx(s);
540 return EWOULDBLOCK;
541 }
542 sc->gkb_flags |= KB_ASLEEP;
543 error = tsleep((caddr_t)sc, PZERO | PCATCH, "kbdrea", 0);
544 kbd = kbd_get_keyboard(KBD_INDEX(dev));
545 if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
546 splx(s);
547 return ENXIO; /* our keyboard has gone... */
548 }
549 if (error) {
550 sc->gkb_flags &= ~KB_ASLEEP;
551 splx(s);
552 return error;
553 }
554 }
555 splx(s);
556
557 /* copy as much input as possible */
558 error = 0;
559 while (uio->uio_resid > 0) {
560 len = imin(uio->uio_resid, sizeof(buffer));
561 len = q_to_b(&sc->gkb_q, buffer, len);
562 if (len <= 0)
563 break;
564 error = uiomove(buffer, len, uio);
565 if (error)
566 break;
567 }
568
569 return error;
570}
571
572static int
573genkbdwrite(dev_t dev, struct uio *uio, int flag)
574{
575 keyboard_t *kbd;
576
577 kbd = kbd_get_keyboard(KBD_INDEX(dev));
578 if ((kbd == NULL) || !KBD_IS_VALID(kbd))
579 return ENXIO;
580 return ENODEV;
581}
582
583static int
584genkbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
585{
586 keyboard_t *kbd;
587 int error;
588
589 kbd = kbd_get_keyboard(KBD_INDEX(dev));
590 if ((kbd == NULL) || !KBD_IS_VALID(kbd))
591 return ENXIO;
592 error = (*kbdsw[kbd->kb_index]->ioctl)(kbd, cmd, arg);
593 if (error == ENOIOCTL)
594 error = ENODEV;
595 return error;
596}
597
598static int
599genkbdpoll(dev_t dev, int events, struct proc *p)
600{
601 keyboard_t *kbd;
602 genkbd_softc_t *sc;
603 int revents;
604 int s;
605
606 revents = 0;
607 s = spltty();
608 sc = dev->si_drv1;
609 kbd = kbd_get_keyboard(KBD_INDEX(dev));
610 if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
611 revents = POLLHUP; /* the keyboard has gone */
612 } else if (events & (POLLIN | POLLRDNORM)) {
613 if (sc->gkb_q.c_cc > 0)
614 revents = events & (POLLIN | POLLRDNORM);
615 else
616 selrecord(p, &sc->gkb_rsel);
617 }
618 splx(s);
619 return revents;
620}
621
622static int
623genkbd_event(keyboard_t *kbd, int event, void *arg)
624{
625 genkbd_softc_t *sc;
626 size_t len;
627 u_char *cp;
628 int mode;
629 int c;
630
631 /* assert(KBD_IS_VALID(kbd)) */
632 sc = (genkbd_softc_t *)arg;
633
634 switch (event) {
635 case KBDIO_KEYINPUT:
636 break;
637 case KBDIO_UNLOADING:
638 /* the keyboard is going... */
639 kbd_release(kbd, (void *)sc);
640 if (sc->gkb_flags & KB_ASLEEP) {
641 sc->gkb_flags &= ~KB_ASLEEP;
642 wakeup((caddr_t)sc);
643 }
644 selwakeup(&sc->gkb_rsel);
645 return 0;
646 default:
647 return EINVAL;
648 }
649
650 /* obtain the current key input mode */
651 if ((*kbdsw[kbd->kb_index]->ioctl)(kbd, KDGKBMODE, (caddr_t)&mode))
652 mode = K_XLATE;
653
654 /* read all pending input */
655 while ((*kbdsw[kbd->kb_index]->check_char)(kbd)) {
656 c = (*kbdsw[kbd->kb_index]->read_char)(kbd, FALSE);
657 if (c == NOKEY)
658 continue;
659 if (c == ERRKEY) /* XXX: ring bell? */
660 continue;
661 if (!KBD_IS_BUSY(kbd))
662 /* the device is not open, discard the input */
663 continue;
664
665 /* store the byte as is for K_RAW and K_CODE modes */
666 if (mode != K_XLATE) {
667 putc(KEYCHAR(c), &sc->gkb_q);
668 continue;
669 }
670
671 /* K_XLATE */
672 if (c & RELKEY) /* key release is ignored */
673 continue;
674
675 /* process special keys; most of them are just ignored... */
676 if (c & SPCLKEY) {
677 switch (KEYCHAR(c)) {
676 /* locking keys */
677 case NLK: case CLK: case SLK: case ALK:
678 /* shift keys */
679 case LSH: case RSH: case LCTR: case RCTR:
680 case LALT: case RALT: case ASH: case META:
681 /* other special keys */
682 case NOP: case SPSC: case RBT: case SUSP:
683 case STBY: case DBG: case NEXT:
678 default:
679 /* ignore them... */
680 continue;
681 case BTAB: /* a backtab: ESC [ Z */
682 putc(0x1b, &sc->gkb_q);
683 putc('[', &sc->gkb_q);
684 putc('Z', &sc->gkb_q);
685 continue;
686 }
687 }
688
689 /* normal chars, normal chars with the META, function keys */
690 switch (KEYFLAGS(c)) {
691 case 0: /* a normal char */
692 putc(KEYCHAR(c), &sc->gkb_q);
693 break;
694 case MKEY: /* the META flag: prepend ESC */
695 putc(0x1b, &sc->gkb_q);
696 putc(KEYCHAR(c), &sc->gkb_q);
697 break;
698 case FKEY | SPCLKEY: /* a function key, return string */
699 cp = (*kbdsw[kbd->kb_index]->get_fkeystr)(kbd,
700 KEYCHAR(c), &len);
701 if (cp != NULL) {
702 while (len-- > 0)
703 putc(*cp++, &sc->gkb_q);
704 }
705 break;
706 }
707 }
708
709 /* wake up sleeping/polling processes */
710 if (sc->gkb_q.c_cc > 0) {
711 if (sc->gkb_flags & KB_ASLEEP) {
712 sc->gkb_flags &= ~KB_ASLEEP;
713 wakeup((caddr_t)sc);
714 }
715 selwakeup(&sc->gkb_rsel);
716 }
717
718 return 0;
719}
720
721#endif /* KBD_INSTALL_CDEV */
722
723/*
724 * Generic low-level keyboard functions
725 * The low-level functions in the keyboard subdriver may use these
726 * functions.
727 */
728
729int
730genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
731{
732 keyarg_t *keyp;
733 fkeyarg_t *fkeyp;
734 int s;
735 int i;
736
737 s = spltty();
738 switch (cmd) {
739
740 case KDGKBINFO: /* get keyboard information */
741 ((keyboard_info_t *)arg)->kb_index = kbd->kb_index;
742 i = imin(strlen(kbd->kb_name) + 1,
743 sizeof(((keyboard_info_t *)arg)->kb_name));
744 bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i);
745 ((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit;
746 ((keyboard_info_t *)arg)->kb_type = kbd->kb_type;
747 ((keyboard_info_t *)arg)->kb_config = kbd->kb_config;
748 ((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags;
749 break;
750
751 case KDGKBTYPE: /* get keyboard type */
752 *(int *)arg = kbd->kb_type;
753 break;
754
755 case GIO_KEYMAP: /* get keyboard translation table */
756 bcopy(kbd->kb_keymap, arg, sizeof(*kbd->kb_keymap));
757 break;
758 case PIO_KEYMAP: /* set keyboard translation table */
759#ifndef KBD_DISABLE_KEYMAP_LOAD
760 bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
761 bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
762 break;
763#else
764 splx(s);
765 return ENODEV;
766#endif
767
768 case GIO_KEYMAPENT: /* get keyboard translation table entry */
769 keyp = (keyarg_t *)arg;
770 if (keyp->keynum >= sizeof(kbd->kb_keymap->key)
771 /sizeof(kbd->kb_keymap->key[0])) {
772 splx(s);
773 return EINVAL;
774 }
775 bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key,
776 sizeof(keyp->key));
777 break;
778 case PIO_KEYMAPENT: /* set keyboard translation table entry */
779#ifndef KBD_DISABLE_KEYMAP_LOAD
780 keyp = (keyarg_t *)arg;
781 if (keyp->keynum >= sizeof(kbd->kb_keymap->key)
782 /sizeof(kbd->kb_keymap->key[0])) {
783 splx(s);
784 return EINVAL;
785 }
786 bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
787 sizeof(keyp->key));
788 break;
789#else
790 splx(s);
791 return ENODEV;
792#endif
793
794 case GIO_DEADKEYMAP: /* get accent key translation table */
795 bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap));
796 break;
797 case PIO_DEADKEYMAP: /* set accent key translation table */
798#ifndef KBD_DISABLE_KEYMAP_LOAD
799 bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
800 break;
801#else
802 splx(s);
803 return ENODEV;
804#endif
805
806 case GETFKEY: /* get functionkey string */
807 fkeyp = (fkeyarg_t *)arg;
808 if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
809 splx(s);
810 return EINVAL;
811 }
812 bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef,
813 kbd->kb_fkeytab[fkeyp->keynum].len);
814 fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len;
815 break;
816 case SETFKEY: /* set functionkey string */
817#ifndef KBD_DISABLE_KEYMAP_LOAD
818 fkeyp = (fkeyarg_t *)arg;
819 if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
820 splx(s);
821 return EINVAL;
822 }
823 kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK);
824 bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
825 kbd->kb_fkeytab[fkeyp->keynum].len);
826 break;
827#else
828 splx(s);
829 return ENODEV;
830#endif
831
832 default:
833 splx(s);
834 return ENOIOCTL;
835 }
836
837 splx(s);
838 return 0;
839}
840
841/* get a pointer to the string associated with the given function key */
842u_char
843*genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
844{
845 if (kbd == NULL)
846 return NULL;
847 fkey -= F_FN;
848 if (fkey > kbd->kb_fkeytab_size)
849 return NULL;
850 *len = kbd->kb_fkeytab[fkey].len;
851 return kbd->kb_fkeytab[fkey].str;
852}
853
854/* diagnostic dump */
855static char
856*get_kbd_type_name(int type)
857{
858 static struct {
859 int type;
860 char *name;
861 } name_table[] = {
862 { KB_84, "AT 84" },
863 { KB_101, "AT 101/102" },
864 { KB_OTHER, "generic" },
865 };
866 int i;
867
868 for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) {
869 if (type == name_table[i].type)
870 return name_table[i].name;
871 }
872 return "unknown";
873}
874
875void
876genkbd_diag(keyboard_t *kbd, int level)
877{
878 if (level > 0) {
879 printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x",
880 kbd->kb_index, kbd->kb_name, kbd->kb_unit,
881 get_kbd_type_name(kbd->kb_type), kbd->kb_type,
882 kbd->kb_config, kbd->kb_flags);
883 if (kbd->kb_io_base > 0)
884 printf(", port:0x%x-0x%x", kbd->kb_io_base,
885 kbd->kb_io_base + kbd->kb_io_size - 1);
886 printf("\n");
887 }
888}
889
890#define set_lockkey_state(k, s, l) \
891 if (!((s) & l ## DOWN)) { \
892 int i; \
893 (s) |= l ## DOWN; \
894 (s) ^= l ## ED; \
895 i = (s) & LOCK_MASK; \
896 (*kbdsw[(k)->kb_index]->ioctl)((k), KDSETLED, (caddr_t)&i); \
897 }
898
899static u_int
900save_accent_key(keyboard_t *kbd, u_int key, int *accents)
901{
902 int i;
903
904 /* make an index into the accent map */
905 i = key - F_ACC + 1;
906 if ((i > kbd->kb_accentmap->n_accs)
907 || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) {
908 /* the index is out of range or pointing to an empty entry */
909 *accents = 0;
910 return ERRKEY;
911 }
912
913 /*
914 * If the same accent key has been hit twice, produce the accent char
915 * itself.
916 */
917 if (i == *accents) {
918 key = kbd->kb_accentmap->acc[i - 1].accchar;
919 *accents = 0;
920 return key;
921 }
922
923 /* remember the index and wait for the next key */
924 *accents = i;
925 return NOKEY;
926}
927
928static u_int
929make_accent_char(keyboard_t *kbd, u_int ch, int *accents)
930{
931 struct acc_t *acc;
932 int i;
933
934 acc = &kbd->kb_accentmap->acc[*accents - 1];
935 *accents = 0;
936
937 /*
938 * If the accent key is followed by the space key,
939 * produce the accent char itself.
940 */
941 if (ch == ' ')
942 return acc->accchar;
943
944 /* scan the accent map */
945 for (i = 0; i < NUM_ACCENTCHARS; ++i) {
946 if (acc->map[i][0] == 0) /* end of table */
947 break;
948 if (acc->map[i][0] == ch)
949 return acc->map[i][1];
950 }
951 /* this char cannot be accented... */
952 return ERRKEY;
953}
954
955int
956genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate,
957 int *accents)
958{
959 struct keyent_t *key;
960 int state = *shiftstate;
961 int action;
962 int f;
963 int i;
964
965 i = keycode;
966 f = state & (AGRS | ALKED);
967 if ((f == AGRS1) || (f == AGRS2) || (f == ALKED))
972 keycode += ALTGR_OFFSET;
973 key = &kbd->kb_keymap->key[keycode];
968 i += ALTGR_OFFSET;
969 key = &kbd->kb_keymap->key[i];
970 i = ((state & SHIFTS) ? 1 : 0)
971 | ((state & CTLS) ? 2 : 0)
972 | ((state & ALTS) ? 4 : 0);
973 if (((key->flgs & FLAG_LOCK_C) && (state & CLKED))
974 || ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) )
975 i ^= 1;
976
977 action = key->map[i];
978 if (up) { /* break: key released */
979 if (key->spcl & (0x80 >> i)) {
980 /* special keys */
981 switch (action) {
982 case LSHA:
983 if (kbd->kb_prev_key == keycode) {
984 set_lockkey_state(kbd, state, ALK);
985 state &= ~ALKDOWN;
986 }
987 action = LSH;
988 /* FALL THROUGH */
989 case LSH:
990 state &= ~SHIFTS1;
991 break;
992 case RSHA:
993 if (kbd->kb_prev_key == keycode) {
994 set_lockkey_state(kbd, state, ALK);
995 state &= ~ALKDOWN;
996 }
997 action = RSH;
998 /* FALL THROUGH */
999 case RSH:
1000 state &= ~SHIFTS2;
1001 break;
1002 case LCTRA:
1003 if (kbd->kb_prev_key == keycode) {
1004 set_lockkey_state(kbd, state, ALK);
1005 state &= ~ALKDOWN;
1006 }
1007 action = LCTR;
1008 /* FALL THROUGH */
1009 case LCTR:
1010 state &= ~CTLS1;
1011 break;
1012 case RCTRA:
1013 if (kbd->kb_prev_key == keycode) {
1014 set_lockkey_state(kbd, state, ALK);
1015 state &= ~ALKDOWN;
1016 }
1017 action = RCTR;
1018 /* FALL THROUGH */
1019 case RCTR:
1020 state &= ~CTLS2;
1021 break;
1022 case LALTA:
1023 if (kbd->kb_prev_key == keycode) {
1024 set_lockkey_state(kbd, state, ALK);
1025 state &= ~ALKDOWN;
1026 }
1027 action = LALT;
1028 /* FALL THROUGH */
1029 case LALT:
1030 state &= ~ALTS1;
1031 break;
1032 case RALTA:
1033 if (kbd->kb_prev_key == keycode) {
1034 set_lockkey_state(kbd, state, ALK);
1035 state &= ~ALKDOWN;
1036 }
1037 action = RALT;
1038 /* FALL THROUGH */
1039 case RALT:
1040 state &= ~ALTS2;
1041 break;
1042 case ASH:
1043 state &= ~AGRS1;
1044 break;
1045 case META:
1046 state &= ~METAS1;
1047 break;
1048 case NLK:
1049 state &= ~NLKDOWN;
1050 break;
1051 case CLK:
1052#ifndef PC98
1053 state &= ~CLKDOWN;
1054#else
1055 state &= ~CLKED;
1056 i = state & LOCK_MASK;
1057 (*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED,
1058 (caddr_t)&i);
1059#endif
1060 break;
1061 case SLK:
1062 state &= ~SLKDOWN;
1063 break;
1064 case ALK:
1065 state &= ~ALKDOWN;
1066 break;
1067 }
1068 *shiftstate = state;
1069 return (SPCLKEY | RELKEY | action);
1070 }
1071 /* release events of regular keys are not reported */
1072 return NOKEY;
1073 } else { /* make: key pressed */
1074 if (key->spcl & (0x80 >> i)) {
1075 /* special keys */
1076 switch (action) {
1077 /* LOCKING KEYS */
1078 case NLK:
1079 set_lockkey_state(kbd, state, NLK);
1080 break;
1081 case CLK:
1082#ifndef PC98
1083 set_lockkey_state(kbd, state, CLK);
1084#else
1085 state |= CLKED;
1086 i = state & LOCK_MASK;
1087 (*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED,
1088 (caddr_t)&i);
1089#endif
1090 break;
1091 case SLK:
1092 set_lockkey_state(kbd, state, SLK);
1093 break;
1094 case ALK:
1095 set_lockkey_state(kbd, state, ALK);
1096 break;
1097 /* NON-LOCKING KEYS */
1098 case SPSC: case RBT: case SUSP: case STBY:
1061 case DBG: case NEXT:
1099 case DBG: case NEXT: case PREV: case PNC:
1100 *accents = 0;
1101 break;
1102 case BTAB:
1103 *accents = 0;
1104 action |= BKEY;
1105 break;
1106 case LSHA:
1107 action = LSH;
1108 /* FALL THROUGH */
1109 case LSH:
1110 state |= SHIFTS1;
1111 break;
1112 case RSHA:
1113 action = RSH;
1114 /* FALL THROUGH */
1115 case RSH:
1116 state |= SHIFTS2;
1117 break;
1118 case LCTRA:
1119 action = LCTR;
1120 /* FALL THROUGH */
1121 case LCTR:
1122 state |= CTLS1;
1123 break;
1124 case RCTRA:
1125 action = RCTR;
1126 /* FALL THROUGH */
1127 case RCTR:
1128 state |= CTLS2;
1129 break;
1130 case LALTA:
1131 action = LALT;
1132 /* FALL THROUGH */
1133 case LALT:
1134 state |= ALTS1;
1135 break;
1136 case RALTA:
1137 action = RALT;
1138 /* FALL THROUGH */
1139 case RALT:
1140 state |= ALTS2;
1141 break;
1142 case ASH:
1143 state |= AGRS1;
1144 break;
1145 case META:
1146 state |= METAS1;
1147 break;
1148 default:
1149 /* is this an accent (dead) key? */
1150 if (action >= F_ACC && action <= L_ACC) {
1151 action = save_accent_key(kbd, action,
1152 accents);
1153 switch (action) {
1154 case NOKEY:
1155 case ERRKEY:
1156 return action;
1157 default:
1158 if (state & METAS)
1159 return (action | MKEY);
1160 else
1161 return action;
1162 }
1163 /* NOT REACHED */
1164 }
1165 /* other special keys */
1166 if (*accents > 0) {
1167 *accents = 0;
1168 return ERRKEY;
1169 }
1170 if (action >= F_FN && action <= L_FN)
1171 action |= FKEY;
1172 /* XXX: return fkey string for the FKEY? */
1173 break;
1174 }
1175 *shiftstate = state;
1176 return (SPCLKEY | action);
1177 } else {
1178 /* regular keys */
1179 if (*accents > 0) {
1180 /* make an accented char */
1181 action = make_accent_char(kbd, action, accents);
1182 if (action == ERRKEY)
1183 return action;
1184 }
1185 if (state & METAS)
1186 action |= MKEY;
1187 return action;
1188 }
1189 }
1190 /* NOT REACHED */
1191}