kbd.c revision 50477
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 50477 1999-08-28 01:08:13Z peter $
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}
140
141void
142kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
143	     fkeytab_t *fkeymap, int fkeymap_size)
144{
145	kbd->kb_keymap = keymap;
146	kbd->kb_accentmap = accmap;
147	kbd->kb_fkeytab = fkeymap;
148	kbd->kb_fkeytab_size = fkeymap_size;
149}
150
151/* register a keyboard and associate it with a function table */
152int
153kbd_register(keyboard_t *kbd)
154{
155	const keyboard_driver_t **list;
156	const keyboard_driver_t *p;
157	int index;
158
159	for (index = 0; index < keyboards; ++index) {
160		if (keyboard[index] == NULL)
161			break;
162	}
163	if (index >= keyboards) {
164		if (kbd_realloc_array())
165			return -1;
166	}
167
168	kbd->kb_index = index;
169	KBD_UNBUSY(kbd);
170	KBD_VALID(kbd);
171	kbd->kb_active = 0;	/* disabled until someone calls kbd_enable() */
172	kbd->kb_token = NULL;
173	kbd->kb_callback.kc_func = NULL;
174	kbd->kb_callback.kc_arg = NULL;
175
176	list = (const keyboard_driver_t **)kbddriver_set.ls_items;
177	while ((p = *list++) != NULL) {
178		if (strcmp(p->name, kbd->kb_name) == 0) {
179			keyboard[index] = kbd;
180			kbdsw[index] = p->kbdsw;
181			return index;
182		}
183	}
184
185	return -1;
186}
187
188int
189kbd_unregister(keyboard_t *kbd)
190{
191	int error;
192	int s;
193
194	if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards))
195		return ENOENT;
196	if (keyboard[kbd->kb_index] != kbd)
197		return ENOENT;
198
199	s = spltty();
200	if (KBD_IS_BUSY(kbd)) {
201		error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING,
202						    kbd->kb_callback.kc_arg);
203		if (error) {
204			splx(s);
205			return error;
206		}
207		if (KBD_IS_BUSY(kbd)) {
208			splx(s);
209			return EBUSY;
210		}
211	}
212	KBD_INVALID(kbd);
213	keyboard[kbd->kb_index] = NULL;
214	kbdsw[kbd->kb_index] = NULL;
215
216	splx(s);
217	return 0;
218}
219
220/* find a funciton table by the driver name */
221keyboard_switch_t
222*kbd_get_switch(char *driver)
223{
224	const keyboard_driver_t **list;
225	const keyboard_driver_t *p;
226
227	list = (const keyboard_driver_t **)kbddriver_set.ls_items;
228	while ((p = *list++) != NULL) {
229		if (strcmp(p->name, driver) == 0)
230			return p->kbdsw;
231	}
232
233	return NULL;
234}
235
236/*
237 * Keyboard client functions
238 * Keyboard clients, such as the console driver `syscons' and the keyboard
239 * cdev driver, use these functions to claim and release a keyboard for
240 * exclusive use.
241 */
242
243/* find the keyboard specified by a driver name and a unit number */
244int
245kbd_find_keyboard(char *driver, int unit)
246{
247	int i;
248
249	for (i = 0; i < keyboards; ++i) {
250		if (keyboard[i] == NULL)
251			continue;
252		if (!KBD_IS_VALID(keyboard[i]))
253			continue;
254		if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver))
255			continue;
256		if ((unit != -1) && (keyboard[i]->kb_unit != unit))
257			continue;
258		return i;
259	}
260	return -1;
261}
262
263/* allocate a keyboard */
264int
265kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func,
266	     void *arg)
267{
268	int index;
269	int s;
270
271	if (func == NULL)
272		return -1;
273
274	s = spltty();
275	index = kbd_find_keyboard(driver, unit);
276	if (index >= 0) {
277		if (KBD_IS_BUSY(keyboard[index])) {
278			splx(s);
279			return -1;
280		}
281		keyboard[index]->kb_token = id;
282		KBD_BUSY(keyboard[index]);
283		keyboard[index]->kb_callback.kc_func = func;
284		keyboard[index]->kb_callback.kc_arg = arg;
285		(*kbdsw[index]->clear_state)(keyboard[index]);
286	}
287	splx(s);
288	return index;
289}
290
291int
292kbd_release(keyboard_t *kbd, void *id)
293{
294	int error;
295	int s;
296
297	s = spltty();
298	if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
299		error = EINVAL;
300	} else if (kbd->kb_token != id) {
301		error = EPERM;
302	} else {
303		kbd->kb_token = NULL;
304		KBD_UNBUSY(kbd);
305		kbd->kb_callback.kc_func = NULL;
306		kbd->kb_callback.kc_arg = NULL;
307		(*kbdsw[kbd->kb_index]->clear_state)(kbd);
308		error = 0;
309	}
310	splx(s);
311	return error;
312}
313
314int
315kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func,
316		    void *arg)
317{
318	int error;
319	int s;
320
321	s = spltty();
322	if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
323		error = EINVAL;
324	} else if (kbd->kb_token != id) {
325		error = EPERM;
326	} else if (func == NULL) {
327		error = EINVAL;
328	} else {
329		kbd->kb_callback.kc_func = func;
330		kbd->kb_callback.kc_arg = arg;
331		error = 0;
332	}
333	splx(s);
334	return error;
335}
336
337/* get a keyboard structure */
338keyboard_t
339*kbd_get_keyboard(int index)
340{
341	if ((index < 0) || (index >= keyboards))
342		return NULL;
343	if (keyboard[index] == NULL)
344		return NULL;
345	if (!KBD_IS_VALID(keyboard[index]))
346		return NULL;
347	return keyboard[index];
348}
349
350/*
351 * The back door for the console driver; configure keyboards
352 * This function is for the kernel console to initialize keyboards
353 * at very early stage.
354 */
355
356int
357kbd_configure(int flags)
358{
359	const keyboard_driver_t **list;
360	const keyboard_driver_t *p;
361
362	list = (const keyboard_driver_t **)kbddriver_set.ls_items;
363	while ((p = *list++) != NULL) {
364		if (p->configure != NULL)
365			(*p->configure)(flags);
366	}
367
368	return 0;
369}
370
371#ifdef KBD_INSTALL_CDEV
372
373/*
374 * Virtual keyboard cdev driver functions
375 * The virtual keyboard driver dispatches driver functions to
376 * appropriate subdrivers.
377 */
378
379#define KBD_UNIT(dev)	minor(dev)
380
381static d_open_t		genkbdopen;
382static d_close_t	genkbdclose;
383static d_read_t		genkbdread;
384static d_write_t	genkbdwrite;
385static d_ioctl_t	genkbdioctl;
386static d_poll_t		genkbdpoll;
387
388#define CDEV_MAJOR	112
389
390static struct cdevsw kbd_cdevsw = {
391	/* open */	genkbdopen,
392	/* close */	genkbdclose,
393	/* read */	genkbdread,
394	/* write */	genkbdwrite,
395	/* ioctl */	genkbdioctl,
396	/* stop */	nostop,
397	/* reset */	noreset,
398	/* devtotty */	nodevtotty,
399	/* poll */	genkbdpoll,
400	/* mmap */	nommap,
401	/* strategy */	nostrategy,
402	/* name */	"kbd",
403	/* parms */	noparms,
404	/* maj */	CDEV_MAJOR,
405	/* dump */	nodump,
406	/* psize */	nopsize,
407	/* flags */	0,
408	/* maxio */	0,
409	/* bmaj */	-1
410};
411
412int
413kbd_attach(keyboard_t *kbd)
414{
415	dev_t dev;
416
417	if (kbd->kb_index >= keyboards)
418		return EINVAL;
419	if (keyboard[kbd->kb_index] != kbd)
420		return EINVAL;
421
422	dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL, 0600,
423		       "kbd%r", kbd->kb_index);
424	if (dev->si_drv1 == NULL)
425		dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF,
426				      M_WAITOK);
427	bzero(dev->si_drv1, sizeof(genkbd_softc_t));
428
429	printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit);
430	return 0;
431}
432
433int
434kbd_detach(keyboard_t *kbd)
435{
436	if (kbd->kb_index >= keyboards)
437		return EINVAL;
438	if (keyboard[kbd->kb_index] != kbd)
439		return EINVAL;
440
441	/* XXX: unmake_dev() ? */
442	return 0;
443}
444
445/*
446 * Generic keyboard cdev driver functions
447 * Keyboard subdrivers may call these functions to implement common
448 * driver functions.
449 */
450
451#define KB_QSIZE	512
452#define KB_BUFSIZE	64
453
454static kbd_callback_func_t genkbd_event;
455
456static int
457genkbdopen(dev_t dev, int mode, int flag, struct proc *p)
458{
459	keyboard_t *kbd;
460	genkbd_softc_t *sc;
461	int s;
462	int i;
463
464	s = spltty();
465	sc = dev->si_drv1;
466	kbd = kbd_get_keyboard(KBD_INDEX(dev));
467	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
468		splx(s);
469		return ENXIO;
470	}
471	i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc,
472			 genkbd_event, (void *)sc);
473	if (i < 0) {
474		splx(s);
475		return EBUSY;
476	}
477	/* assert(i == kbd->kb_index) */
478	/* assert(kbd == kbd_get_keyboard(i)) */
479
480	/*
481	 * NOTE: even when we have successfully claimed a keyboard,
482	 * the device may still be missing (!KBD_HAS_DEVICE(kbd)).
483	 */
484
485#if 0
486	bzero(&sc->gkb_q, sizeof(sc->gkb_q));
487#endif
488	clist_alloc_cblocks(&sc->gkb_q, KB_QSIZE, KB_QSIZE/2); /* XXX */
489	sc->gkb_rsel.si_flags = 0;
490	sc->gkb_rsel.si_pid = 0;
491	splx(s);
492
493	return 0;
494}
495
496static int
497genkbdclose(dev_t dev, int mode, int flag, struct proc *p)
498{
499	keyboard_t *kbd;
500	genkbd_softc_t *sc;
501	int s;
502
503	/*
504	 * NOTE: the device may have already become invalid.
505	 * kbd == NULL || !KBD_IS_VALID(kbd)
506	 */
507	s = spltty();
508	sc = dev->si_drv1;
509	kbd = kbd_get_keyboard(KBD_INDEX(dev));
510	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
511		/* XXX: we shall be forgiving and don't report error... */
512	} else {
513		kbd_release(kbd, (void *)sc);
514#if 0
515		clist_free_cblocks(&sc->gkb_q);
516#endif
517	}
518	splx(s);
519	return 0;
520}
521
522static int
523genkbdread(dev_t dev, struct uio *uio, int flag)
524{
525	keyboard_t *kbd;
526	genkbd_softc_t *sc;
527	u_char buffer[KB_BUFSIZE];
528	int len;
529	int error;
530	int s;
531
532	/* wait for input */
533	s = spltty();
534	sc = dev->si_drv1;
535	kbd = kbd_get_keyboard(KBD_INDEX(dev));
536	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
537		splx(s);
538		return ENXIO;
539	}
540	while (sc->gkb_q.c_cc == 0) {
541		if (flag & IO_NDELAY) {
542			splx(s);
543			return EWOULDBLOCK;
544		}
545		sc->gkb_flags |= KB_ASLEEP;
546		error = tsleep((caddr_t)sc, PZERO | PCATCH, "kbdrea", 0);
547		kbd = kbd_get_keyboard(KBD_INDEX(dev));
548		if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
549			splx(s);
550			return ENXIO;	/* our keyboard has gone... */
551		}
552		if (error) {
553			sc->gkb_flags &= ~KB_ASLEEP;
554			splx(s);
555			return error;
556		}
557	}
558	splx(s);
559
560	/* copy as much input as possible */
561	error = 0;
562	while (uio->uio_resid > 0) {
563		len = imin(uio->uio_resid, sizeof(buffer));
564		len = q_to_b(&sc->gkb_q, buffer, len);
565		if (len <= 0)
566			break;
567		error = uiomove(buffer, len, uio);
568		if (error)
569			break;
570	}
571
572	return error;
573}
574
575static int
576genkbdwrite(dev_t dev, struct uio *uio, int flag)
577{
578	keyboard_t *kbd;
579
580	kbd = kbd_get_keyboard(KBD_INDEX(dev));
581	if ((kbd == NULL) || !KBD_IS_VALID(kbd))
582		return ENXIO;
583	return ENODEV;
584}
585
586static int
587genkbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
588{
589	keyboard_t *kbd;
590	int error;
591
592	kbd = kbd_get_keyboard(KBD_INDEX(dev));
593	if ((kbd == NULL) || !KBD_IS_VALID(kbd))
594		return ENXIO;
595	error = (*kbdsw[kbd->kb_index]->ioctl)(kbd, cmd, arg);
596	if (error == ENOIOCTL)
597		error = ENODEV;
598	return error;
599}
600
601static int
602genkbdpoll(dev_t dev, int events, struct proc *p)
603{
604	keyboard_t *kbd;
605	genkbd_softc_t *sc;
606	int revents;
607	int s;
608
609	revents = 0;
610	s = spltty();
611	sc = dev->si_drv1;
612	kbd = kbd_get_keyboard(KBD_INDEX(dev));
613	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
614		revents =  POLLHUP;	/* the keyboard has gone */
615	} else if (events & (POLLIN | POLLRDNORM)) {
616		if (sc->gkb_q.c_cc > 0)
617			revents = events & (POLLIN | POLLRDNORM);
618		else
619			selrecord(p, &sc->gkb_rsel);
620	}
621	splx(s);
622	return revents;
623}
624
625static int
626genkbd_event(keyboard_t *kbd, int event, void *arg)
627{
628	genkbd_softc_t *sc;
629	size_t len;
630	u_char *cp;
631	int mode;
632	int c;
633
634	/* assert(KBD_IS_VALID(kbd)) */
635	sc = (genkbd_softc_t *)arg;
636
637	switch (event) {
638	case KBDIO_KEYINPUT:
639		break;
640	case KBDIO_UNLOADING:
641		/* the keyboard is going... */
642		kbd_release(kbd, (void *)sc);
643		if (sc->gkb_flags & KB_ASLEEP) {
644			sc->gkb_flags &= ~KB_ASLEEP;
645			wakeup((caddr_t)sc);
646		}
647		selwakeup(&sc->gkb_rsel);
648		return 0;
649	default:
650		return EINVAL;
651	}
652
653	/* obtain the current key input mode */
654	if ((*kbdsw[kbd->kb_index]->ioctl)(kbd, KDGKBMODE, (caddr_t)&mode))
655		mode = K_XLATE;
656
657	/* read all pending input */
658	while ((*kbdsw[kbd->kb_index]->check_char)(kbd)) {
659		c = (*kbdsw[kbd->kb_index]->read_char)(kbd, FALSE);
660		if (c == NOKEY)
661			continue;
662		if (c == ERRKEY)	/* XXX: ring bell? */
663			continue;
664		if (!KBD_IS_BUSY(kbd))
665			/* the device is not open, discard the input */
666			continue;
667
668		/* store the byte as is for K_RAW and K_CODE modes */
669		if (mode != K_XLATE) {
670			putc(KEYCHAR(c), &sc->gkb_q);
671			continue;
672		}
673
674		/* K_XLATE */
675		if (c & RELKEY)	/* key release is ignored */
676			continue;
677
678		/* process special keys; most of them are just ignored... */
679		if (c & SPCLKEY) {
680			switch (KEYCHAR(c)) {
681			/* locking keys */
682			case NLK:  case CLK:  case SLK:  case ALK:
683			/* shift keys */
684			case LSH:  case RSH:  case LCTR: case RCTR:
685			case LALT: case RALT: case ASH:  case META:
686			/* other special keys */
687			case NOP:  case SPSC: case RBT:  case SUSP:
688			case STBY: case DBG:  case NEXT:
689				/* ignore them... */
690				continue;
691			case BTAB:	/* a backtab: ESC [ Z */
692				putc(0x1b, &sc->gkb_q);
693				putc('[', &sc->gkb_q);
694				putc('Z', &sc->gkb_q);
695				continue;
696			}
697		}
698
699		/* normal chars, normal chars with the META, function keys */
700		switch (KEYFLAGS(c)) {
701		case 0:			/* a normal char */
702			putc(KEYCHAR(c), &sc->gkb_q);
703			break;
704		case MKEY:		/* the META flag: prepend ESC */
705			putc(0x1b, &sc->gkb_q);
706			putc(KEYCHAR(c), &sc->gkb_q);
707			break;
708		case FKEY | SPCLKEY:	/* a function key, return string */
709			cp = (*kbdsw[kbd->kb_index]->get_fkeystr)(kbd,
710							KEYCHAR(c), &len);
711			if (cp != NULL) {
712				while (len-- >  0)
713					putc(*cp++, &sc->gkb_q);
714			}
715			break;
716		}
717	}
718
719	/* wake up sleeping/polling processes */
720	if (sc->gkb_q.c_cc > 0) {
721		if (sc->gkb_flags & KB_ASLEEP) {
722			sc->gkb_flags &= ~KB_ASLEEP;
723			wakeup((caddr_t)sc);
724		}
725		selwakeup(&sc->gkb_rsel);
726	}
727
728	return 0;
729}
730
731#endif /* KBD_INSTALL_CDEV */
732
733/*
734 * Generic low-level keyboard functions
735 * The low-level functions in the keyboard subdriver may use these
736 * functions.
737 */
738
739int
740genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
741{
742	keyarg_t *keyp;
743	fkeyarg_t *fkeyp;
744	int s;
745	int i;
746
747	s = spltty();
748	switch (cmd) {
749
750	case KDGKBINFO:		/* get keyboard information */
751		((keyboard_info_t *)arg)->kb_index = kbd->kb_index;
752		i = imin(strlen(kbd->kb_name) + 1,
753			 sizeof(((keyboard_info_t *)arg)->kb_name));
754		bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i);
755		((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit;
756		((keyboard_info_t *)arg)->kb_type = kbd->kb_type;
757		((keyboard_info_t *)arg)->kb_config = kbd->kb_config;
758		((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags;
759		break;
760
761	case KDGKBTYPE:		/* get keyboard type */
762		*(int *)arg = kbd->kb_type;
763		break;
764
765	case GIO_KEYMAP:	/* get keyboard translation table */
766		bcopy(kbd->kb_keymap, arg, sizeof(*kbd->kb_keymap));
767		break;
768	case PIO_KEYMAP:	/* set keyboard translation table */
769#ifndef KBD_DISABLE_KEYMAP_LOAD
770		bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
771		bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
772		break;
773#else
774		splx(s);
775		return ENODEV;
776#endif
777
778	case GIO_KEYMAPENT:	/* get keyboard translation table entry */
779		keyp = (keyarg_t *)arg;
780		if (keyp->keynum >= sizeof(kbd->kb_keymap->key)
781					/sizeof(kbd->kb_keymap->key[0])) {
782			splx(s);
783			return EINVAL;
784		}
785		bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key,
786		      sizeof(keyp->key));
787		break;
788	case PIO_KEYMAPENT:	/* set keyboard translation table entry */
789#ifndef KBD_DISABLE_KEYMAP_LOAD
790		keyp = (keyarg_t *)arg;
791		if (keyp->keynum >= sizeof(kbd->kb_keymap->key)
792					/sizeof(kbd->kb_keymap->key[0])) {
793			splx(s);
794			return EINVAL;
795		}
796		bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
797		      sizeof(keyp->key));
798		break;
799#else
800		splx(s);
801		return ENODEV;
802#endif
803
804	case GIO_DEADKEYMAP:	/* get accent key translation table */
805		bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap));
806		break;
807	case PIO_DEADKEYMAP:	/* set accent key translation table */
808#ifndef KBD_DISABLE_KEYMAP_LOAD
809		bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
810		break;
811#else
812		splx(s);
813		return ENODEV;
814#endif
815
816	case GETFKEY:		/* get functionkey string */
817		fkeyp = (fkeyarg_t *)arg;
818		if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
819			splx(s);
820			return EINVAL;
821		}
822		bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef,
823		      kbd->kb_fkeytab[fkeyp->keynum].len);
824		fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len;
825		break;
826	case SETFKEY:		/* set functionkey string */
827#ifndef KBD_DISABLE_KEYMAP_LOAD
828		fkeyp = (fkeyarg_t *)arg;
829		if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
830			splx(s);
831			return EINVAL;
832		}
833		kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK);
834		bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
835		      kbd->kb_fkeytab[fkeyp->keynum].len);
836		break;
837#else
838		splx(s);
839		return ENODEV;
840#endif
841
842	default:
843		splx(s);
844		return ENOIOCTL;
845	}
846
847	splx(s);
848	return 0;
849}
850
851/* get a pointer to the string associated with the given function key */
852u_char
853*genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
854{
855	if (kbd == NULL)
856		return NULL;
857	fkey -= F_FN;
858	if (fkey > kbd->kb_fkeytab_size)
859		return NULL;
860	*len = kbd->kb_fkeytab[fkey].len;
861	return kbd->kb_fkeytab[fkey].str;
862}
863
864/* diagnostic dump */
865static char
866*get_kbd_type_name(int type)
867{
868	static struct {
869		int type;
870		char *name;
871	} name_table[] = {
872		{ KB_84,	"AT 84" },
873		{ KB_101,	"AT 101/102" },
874		{ KB_OTHER,	"generic" },
875	};
876	int i;
877
878	for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) {
879		if (type == name_table[i].type)
880			return name_table[i].name;
881	}
882	return "unknown";
883}
884
885void
886genkbd_diag(keyboard_t *kbd, int level)
887{
888	if (level > 0) {
889		printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x",
890		       kbd->kb_index, kbd->kb_name, kbd->kb_unit,
891		       get_kbd_type_name(kbd->kb_type), kbd->kb_type,
892		       kbd->kb_config, kbd->kb_flags);
893		if (kbd->kb_io_base > 0)
894			printf(", port:0x%x-0x%x", kbd->kb_io_base,
895			       kbd->kb_io_base + kbd->kb_io_size - 1);
896		printf("\n");
897	}
898}
899
900#define set_lockkey_state(k, s, l)				\
901	if (!((s) & l ## DOWN)) {				\
902		int i;						\
903		(s) |= l ## DOWN;				\
904		(s) ^= l ## ED;					\
905		i = (s) & LOCK_MASK;				\
906		(*kbdsw[(k)->kb_index]->ioctl)((k), KDSETLED, (caddr_t)&i); \
907	}
908
909static u_int
910save_accent_key(keyboard_t *kbd, u_int key, int *accents)
911{
912	int i;
913
914	/* make an index into the accent map */
915	i = key - F_ACC + 1;
916	if ((i > kbd->kb_accentmap->n_accs)
917	    || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) {
918		/* the index is out of range or pointing to an empty entry */
919		*accents = 0;
920		return ERRKEY;
921	}
922
923	/*
924	 * If the same accent key has been hit twice, produce the accent char
925	 * itself.
926	 */
927	if (i == *accents) {
928		key = kbd->kb_accentmap->acc[i - 1].accchar;
929		*accents = 0;
930		return key;
931	}
932
933	/* remember the index and wait for the next key  */
934	*accents = i;
935	return NOKEY;
936}
937
938static u_int
939make_accent_char(keyboard_t *kbd, u_int ch, int *accents)
940{
941	struct acc_t *acc;
942	int i;
943
944	acc = &kbd->kb_accentmap->acc[*accents - 1];
945	*accents = 0;
946
947	/*
948	 * If the accent key is followed by the space key,
949	 * produce the accent char itself.
950	 */
951	if (ch == ' ')
952		return acc->accchar;
953
954	/* scan the accent map */
955	for (i = 0; i < NUM_ACCENTCHARS; ++i) {
956		if (acc->map[i][0] == 0)	/* end of table */
957			break;
958		if (acc->map[i][0] == ch)
959			return acc->map[i][1];
960	}
961	/* this char cannot be accented... */
962	return ERRKEY;
963}
964
965int
966genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate,
967		 int *accents)
968{
969	struct keyent_t *key;
970	int state = *shiftstate;
971	int action;
972	int f;
973	int i;
974
975	f = state & (AGRS | ALKED);
976	if ((f == AGRS1) || (f == AGRS2) || (f == ALKED))
977		keycode += ALTGR_OFFSET;
978	key = &kbd->kb_keymap->key[keycode];
979	i = ((state & SHIFTS) ? 1 : 0)
980	    | ((state & CTLS) ? 2 : 0)
981	    | ((state & ALTS) ? 4 : 0);
982	if (((key->flgs & FLAG_LOCK_C) && (state & CLKED))
983		|| ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) )
984		i ^= 1;
985
986	action = key->map[i];
987	if (up) {	/* break: key released */
988		if (key->spcl & (0x80 >> i)) {
989			/* special keys */
990			switch (action) {
991			case LSH:
992				state &= ~SHIFTS1;
993				break;
994			case RSH:
995				state &= ~SHIFTS2;
996				break;
997			case LCTR:
998				state &= ~CTLS1;
999				break;
1000			case RCTR:
1001				state &= ~CTLS2;
1002				break;
1003			case LALT:
1004				state &= ~ALTS1;
1005				break;
1006			case RALT:
1007				state &= ~ALTS2;
1008				break;
1009			case ASH:
1010				state &= ~AGRS1;
1011				break;
1012			case META:
1013				state &= ~METAS1;
1014				break;
1015			case NLK:
1016				state &= ~NLKDOWN;
1017				break;
1018			case CLK:
1019#ifndef PC98
1020				state &= ~CLKDOWN;
1021#else
1022				state &= ~CLKED;
1023				i = state & LOCK_MASK;
1024				(*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED,
1025							       (caddr_t)&i);
1026#endif
1027				break;
1028			case SLK:
1029				state &= ~SLKDOWN;
1030				break;
1031			case ALK:
1032				state &= ~ALKDOWN;
1033				break;
1034			}
1035			*shiftstate = state;
1036			return (SPCLKEY | RELKEY | action);
1037		}
1038		/* release events of regular keys are not reported */
1039		return NOKEY;
1040	} else {	/* make: key pressed */
1041		if (key->spcl & (0x80 >> i)) {
1042			/* special keys */
1043			switch (action) {
1044			/* LOCKING KEYS */
1045			case NLK:
1046				set_lockkey_state(kbd, state, NLK);
1047				break;
1048			case CLK:
1049#ifndef PC98
1050				set_lockkey_state(kbd, state, CLK);
1051#else
1052				state |= CLKED;
1053				i = state & LOCK_MASK;
1054				(*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED,
1055							       (caddr_t)&i);
1056#endif
1057				break;
1058			case SLK:
1059				set_lockkey_state(kbd, state, SLK);
1060				break;
1061			case ALK:
1062				set_lockkey_state(kbd, state, ALK);
1063				break;
1064			/* NON-LOCKING KEYS */
1065			case SPSC: case RBT:  case SUSP: case STBY:
1066			case DBG:  case NEXT:
1067				*accents = 0;
1068				break;
1069			case BTAB:
1070				*accents = 0;
1071				action |= BKEY;
1072				break;
1073			case LSH:
1074				state |= SHIFTS1;
1075				break;
1076			case RSH:
1077				state |= SHIFTS2;
1078				break;
1079			case LCTR:
1080				state |= CTLS1;
1081				break;
1082			case RCTR:
1083				state |= CTLS2;
1084				break;
1085			case LALT:
1086				state |= ALTS1;
1087				break;
1088			case RALT:
1089				state |= ALTS2;
1090				break;
1091			case ASH:
1092				state |= AGRS1;
1093				break;
1094			case META:
1095				state |= METAS1;
1096				break;
1097			default:
1098				/* is this an accent (dead) key? */
1099				if (action >= F_ACC && action <= L_ACC) {
1100					action = save_accent_key(kbd, action,
1101								 accents);
1102					switch (action) {
1103					case NOKEY:
1104					case ERRKEY:
1105						return action;
1106					default:
1107						if (state & METAS)
1108							return (action | MKEY);
1109						else
1110							return action;
1111					}
1112					/* NOT REACHED */
1113				}
1114				/* other special keys */
1115				if (*accents > 0) {
1116					*accents = 0;
1117					return ERRKEY;
1118				}
1119				if (action >= F_FN && action <= L_FN)
1120					action |= FKEY;
1121				/* XXX: return fkey string for the FKEY? */
1122			}
1123			*shiftstate = state;
1124			return (SPCLKEY | action);
1125		} else {
1126			/* regular keys */
1127			if (*accents > 0) {
1128				/* make an accented char */
1129				action = make_accent_char(kbd, action, accents);
1130				if (action == ERRKEY)
1131					return action;
1132			}
1133			if (state & METAS)
1134				action |= MKEY;
1135			return action;
1136		}
1137	}
1138	/* NOT REACHED */
1139}
1140