142421Syokota/*-
242421Syokota * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
342421Syokota * All rights reserved.
442421Syokota *
542421Syokota * Redistribution and use in source and binary forms, with or without
642421Syokota * modification, are permitted provided that the following conditions
742421Syokota * are met:
842421Syokota * 1. Redistributions of source code must retain the above copyright
942421Syokota *    notice, this list of conditions and the following disclaimer as
1042421Syokota *    the first lines of this file unmodified.
1142421Syokota * 2. Redistributions in binary form must reproduce the above copyright
1242421Syokota *    notice, this list of conditions and the following disclaimer in the
1342421Syokota *    documentation and/or other materials provided with the distribution.
1442421Syokota *
1542421Syokota * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
1642421Syokota * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1742421Syokota * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1842421Syokota * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1942421Syokota * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2042421Syokota * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2142421Syokota * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2242421Syokota * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2342421Syokota * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2442421Syokota * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2542421Syokota *
2642421Syokota */
2742421Syokota
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD$");
30119418Sobrien
3142421Syokota#include "opt_kbd.h"
3242421Syokota
3342421Syokota#include <sys/param.h>
3442421Syokota#include <sys/systm.h>
3542421Syokota#include <sys/kernel.h>
3642421Syokota#include <sys/malloc.h>
3742421Syokota#include <sys/conf.h>
38139193Sphk#include <sys/fcntl.h>
3942421Syokota#include <sys/poll.h>
40164033Srwatson#include <sys/priv.h>
41112050Sdwmalone#include <sys/proc.h>
42180777Sed#include <sys/selinfo.h>
43112050Sdwmalone#include <sys/sysctl.h>
4442421Syokota#include <sys/uio.h>
4542421Syokota
4666834Sphk#include <sys/kbio.h>
4742421Syokota
4842421Syokota#include <dev/kbd/kbdreg.h>
4942421Syokota
50183397Sed#define KBD_INDEX(dev)	dev2unit(dev)
5150154Syokota
52193512Sed#define KB_QSIZE	512
53193512Sed#define KB_BUFSIZE	64
54193512Sed
5550154Syokotatypedef struct genkbd_softc {
5650154Syokota	int		gkb_flags;	/* flag/status bits */
5750154Syokota#define KB_ASLEEP	(1 << 0)
5850154Syokota	struct selinfo	gkb_rsel;
59193512Sed	char		gkb_q[KB_QSIZE];		/* input queue */
60193512Sed	unsigned int	gkb_q_start;
61193512Sed	unsigned int	gkb_q_length;
6250154Syokota} genkbd_softc_t;
6350154Syokota
6460938Sjakestatic	SLIST_HEAD(, keyboard_driver) keyboard_drivers =
65127751Sdes	SLIST_HEAD_INITIALIZER(keyboard_drivers);
6654545Syokota
6778161SpeterSET_DECLARE(kbddriver_set, const keyboard_driver_t);
6878161Speter
6942421Syokota/* local arrays */
7042421Syokota
7142421Syokota/*
7242421Syokota * We need at least one entry each in order to initialize a keyboard
7342421Syokota * for the kernel console.  The arrays will be increased dynamically
7442421Syokota * when necessary.
7542421Syokota */
7642564Syokota
7742564Syokotastatic int		keyboards = 1;
7842421Syokotastatic keyboard_t	*kbd_ini;
7942564Syokotastatic keyboard_t	**keyboard = &kbd_ini;
8042421Syokotastatic keyboard_switch_t *kbdsw_ini;
8142421Syokota       keyboard_switch_t **kbdsw = &kbdsw_ini;
8242421Syokota
83112050Sdwmalonestatic int keymap_restrict_change;
84248085Smariusstatic SYSCTL_NODE(_hw, OID_AUTO, kbd, CTLFLAG_RD, 0, "kbd");
85112050SdwmaloneSYSCTL_INT(_hw_kbd, OID_AUTO, keymap_restrict_change, CTLFLAG_RW,
86112050Sdwmalone    &keymap_restrict_change, 0, "restrict ability to change keymap");
87112050Sdwmalone
8842421Syokota#define ARRAY_DELTA	4
8942421Syokota
9044628Syokotastatic int
9142421Syokotakbd_realloc_array(void)
9242421Syokota{
9342421Syokota	keyboard_t **new_kbd;
9442421Syokota	keyboard_switch_t **new_kbdsw;
9542421Syokota	int newsize;
9642421Syokota	int s;
9742421Syokota
9842421Syokota	s = spltty();
9942421Syokota	newsize = ((keyboards + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA;
10069781Sdwmalone	new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT|M_ZERO);
10144628Syokota	if (new_kbd == NULL) {
10244628Syokota		splx(s);
103127752Sdes		return (ENOMEM);
10444628Syokota	}
10569781Sdwmalone	new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF,
10669781Sdwmalone			    M_NOWAIT|M_ZERO);
10744628Syokota	if (new_kbdsw == NULL) {
10844628Syokota		free(new_kbd, M_DEVBUF);
10944628Syokota		splx(s);
110127752Sdes		return (ENOMEM);
11144628Syokota	}
11242421Syokota	bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards);
11342421Syokota	bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards);
11442421Syokota	if (keyboards > 1) {
11542421Syokota		free(keyboard, M_DEVBUF);
11642421Syokota		free(kbdsw, M_DEVBUF);
11742421Syokota	}
11842421Syokota	keyboard = new_kbd;
11942421Syokota	kbdsw = new_kbdsw;
12042421Syokota	keyboards = newsize;
12142421Syokota	splx(s);
12242421Syokota
12342421Syokota	if (bootverbose)
12442421Syokota		printf("kbd: new array size %d\n", keyboards);
12544628Syokota
126127752Sdes	return (0);
12742421Syokota}
12842421Syokota
12942421Syokota/*
13042421Syokota * Low-level keyboard driver functions
13142421Syokota * Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard
13242421Syokota * driver, call these functions to initialize the keyboard_t structure
13342421Syokota * and register it to the virtual keyboard driver `kbd'.
13442421Syokota */
13542421Syokota
13642421Syokota/* initialize the keyboard_t structure */
13742421Syokotavoid
13842421Syokotakbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config,
13942421Syokota		int port, int port_size)
14042421Syokota{
14142421Syokota	kbd->kb_flags = KB_NO_DEVICE;	/* device has not been found */
14242421Syokota	kbd->kb_name = name;
14342421Syokota	kbd->kb_type = type;
14442421Syokota	kbd->kb_unit = unit;
14544628Syokota	kbd->kb_config = config & ~KB_CONF_PROBE_ONLY;
14642421Syokota	kbd->kb_led = 0;		/* unknown */
14742421Syokota	kbd->kb_io_base = port;
14842421Syokota	kbd->kb_io_size = port_size;
14942421Syokota	kbd->kb_data = NULL;
15042421Syokota	kbd->kb_keymap = NULL;
15142421Syokota	kbd->kb_accentmap = NULL;
15242421Syokota	kbd->kb_fkeytab = NULL;
15342421Syokota	kbd->kb_fkeytab_size = 0;
15444628Syokota	kbd->kb_delay1 = KB_DELAY1;	/* these values are advisory only */
15544628Syokota	kbd->kb_delay2 = KB_DELAY2;
15654382Syokota	kbd->kb_count = 0L;
15780040Syokota	bzero(kbd->kb_lastact, sizeof(kbd->kb_lastact));
15842421Syokota}
15942421Syokota
16042421Syokotavoid
16142421Syokotakbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
16242421Syokota	     fkeytab_t *fkeymap, int fkeymap_size)
16342421Syokota{
16442421Syokota	kbd->kb_keymap = keymap;
16542421Syokota	kbd->kb_accentmap = accmap;
16642421Syokota	kbd->kb_fkeytab = fkeymap;
16742421Syokota	kbd->kb_fkeytab_size = fkeymap_size;
16842421Syokota}
16942421Syokota
17054545Syokota/* declare a new keyboard driver */
17154545Syokotaint
17254545Syokotakbd_add_driver(keyboard_driver_t *driver)
17354545Syokota{
17454545Syokota	if (SLIST_NEXT(driver, link))
175127752Sdes		return (EINVAL);
17654545Syokota	SLIST_INSERT_HEAD(&keyboard_drivers, driver, link);
177127752Sdes	return (0);
17854545Syokota}
17954545Syokota
18054545Syokotaint
18154545Syokotakbd_delete_driver(keyboard_driver_t *driver)
18254545Syokota{
18360938Sjake	SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link);
18454545Syokota	SLIST_NEXT(driver, link) = NULL;
185127752Sdes	return (0);
18654545Syokota}
18754545Syokota
18842421Syokota/* register a keyboard and associate it with a function table */
18942421Syokotaint
19042421Syokotakbd_register(keyboard_t *kbd)
19142421Syokota{
19247295Syokota	const keyboard_driver_t **list;
19347295Syokota	const keyboard_driver_t *p;
194156126Semax	keyboard_t *mux;
195156126Semax	keyboard_info_t ki;
19642421Syokota	int index;
19742421Syokota
198156126Semax	mux = kbd_get_keyboard(kbd_find_keyboard("kbdmux", -1));
199156126Semax
20042421Syokota	for (index = 0; index < keyboards; ++index) {
20142421Syokota		if (keyboard[index] == NULL)
20242421Syokota			break;
20342421Syokota	}
20444628Syokota	if (index >= keyboards) {
20544628Syokota		if (kbd_realloc_array())
206127752Sdes			return (-1);
20744628Syokota	}
20842421Syokota
20942421Syokota	kbd->kb_index = index;
21042421Syokota	KBD_UNBUSY(kbd);
21142421Syokota	KBD_VALID(kbd);
21242421Syokota	kbd->kb_active = 0;	/* disabled until someone calls kbd_enable() */
21342421Syokota	kbd->kb_token = NULL;
21442421Syokota	kbd->kb_callback.kc_func = NULL;
21542421Syokota	kbd->kb_callback.kc_arg = NULL;
21642421Syokota
21754545Syokota	SLIST_FOREACH(p, &keyboard_drivers, link) {
21854545Syokota		if (strcmp(p->name, kbd->kb_name) == 0) {
21954545Syokota			keyboard[index] = kbd;
22054545Syokota			kbdsw[index] = p->kbdsw;
221156126Semax
222156126Semax			if (mux != NULL) {
223156126Semax				bzero(&ki, sizeof(ki));
224156126Semax				strcpy(ki.kb_name, kbd->kb_name);
225156126Semax				ki.kb_unit = kbd->kb_unit;
226156126Semax
227213770Srpaulo				(void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
228156126Semax			}
229156126Semax
230127752Sdes			return (index);
23154545Syokota		}
23254545Syokota	}
23378161Speter	SET_FOREACH(list, kbddriver_set) {
23478161Speter		p = *list;
23542421Syokota		if (strcmp(p->name, kbd->kb_name) == 0) {
23642421Syokota			keyboard[index] = kbd;
23742421Syokota			kbdsw[index] = p->kbdsw;
238156126Semax
239156126Semax			if (mux != NULL) {
240156126Semax				bzero(&ki, sizeof(ki));
241156126Semax				strcpy(ki.kb_name, kbd->kb_name);
242156126Semax				ki.kb_unit = kbd->kb_unit;
243156126Semax
244213770Srpaulo				(void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
245156126Semax			}
246156126Semax
247127752Sdes			return (index);
24842421Syokota		}
24942421Syokota	}
25042421Syokota
251127752Sdes	return (-1);
25242421Syokota}
25342421Syokota
25442421Syokotaint
25542421Syokotakbd_unregister(keyboard_t *kbd)
25642421Syokota{
25742421Syokota	int error;
25842421Syokota	int s;
25942421Syokota
26042421Syokota	if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards))
261127752Sdes		return (ENOENT);
26242421Syokota	if (keyboard[kbd->kb_index] != kbd)
263127752Sdes		return (ENOENT);
26442421Syokota
26542421Syokota	s = spltty();
26642421Syokota	if (KBD_IS_BUSY(kbd)) {
26742421Syokota		error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING,
268127752Sdes		    kbd->kb_callback.kc_arg);
26942421Syokota		if (error) {
27042421Syokota			splx(s);
271127752Sdes			return (error);
27242421Syokota		}
27342421Syokota		if (KBD_IS_BUSY(kbd)) {
27442421Syokota			splx(s);
275127752Sdes			return (EBUSY);
27642421Syokota		}
27742421Syokota	}
27842421Syokota	KBD_INVALID(kbd);
27942421Syokota	keyboard[kbd->kb_index] = NULL;
28042421Syokota	kbdsw[kbd->kb_index] = NULL;
28142421Syokota
28242421Syokota	splx(s);
283127752Sdes	return (0);
28442421Syokota}
28542421Syokota
28642421Syokota/* find a funciton table by the driver name */
28742421Syokotakeyboard_switch_t
28842421Syokota*kbd_get_switch(char *driver)
28942421Syokota{
29047295Syokota	const keyboard_driver_t **list;
29147295Syokota	const keyboard_driver_t *p;
29242421Syokota
29354545Syokota	SLIST_FOREACH(p, &keyboard_drivers, link) {
29454545Syokota		if (strcmp(p->name, driver) == 0)
295127752Sdes			return (p->kbdsw);
29654545Syokota	}
29778161Speter	SET_FOREACH(list, kbddriver_set) {
29878161Speter		p = *list;
29942421Syokota		if (strcmp(p->name, driver) == 0)
300127752Sdes			return (p->kbdsw);
30142421Syokota	}
30242421Syokota
303127752Sdes	return (NULL);
30442421Syokota}
30542421Syokota
30642421Syokota/*
30742421Syokota * Keyboard client functions
30842421Syokota * Keyboard clients, such as the console driver `syscons' and the keyboard
30942421Syokota * cdev driver, use these functions to claim and release a keyboard for
31042421Syokota * exclusive use.
31142421Syokota */
31242421Syokota
313147980Semax/*
314147980Semax * find the keyboard specified by a driver name and a unit number
315147980Semax * starting at given index
316147980Semax */
31742421Syokotaint
318147980Semaxkbd_find_keyboard2(char *driver, int unit, int index)
31942421Syokota{
32042421Syokota	int i;
32142421Syokota
322147980Semax	if ((index < 0) || (index >= keyboards))
323147980Semax		return (-1);
324147980Semax
325147980Semax	for (i = index; i < keyboards; ++i) {
32642421Syokota		if (keyboard[i] == NULL)
32742421Syokota			continue;
32842421Syokota		if (!KBD_IS_VALID(keyboard[i]))
32942421Syokota			continue;
33042421Syokota		if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver))
33142421Syokota			continue;
33242421Syokota		if ((unit != -1) && (keyboard[i]->kb_unit != unit))
33342421Syokota			continue;
334127752Sdes		return (i);
33542421Syokota	}
336147980Semax
337127752Sdes	return (-1);
33842421Syokota}
33942421Syokota
340147980Semax/* find the keyboard specified by a driver name and a unit number */
341147980Semaxint
342147980Semaxkbd_find_keyboard(char *driver, int unit)
343147980Semax{
344147980Semax	return (kbd_find_keyboard2(driver, unit, 0));
345147980Semax}
346147980Semax
34742421Syokota/* allocate a keyboard */
34842421Syokotaint
34942421Syokotakbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func,
35042421Syokota	     void *arg)
35142421Syokota{
35242421Syokota	int index;
35342421Syokota	int s;
35442421Syokota
35542421Syokota	if (func == NULL)
356127752Sdes		return (-1);
35742421Syokota
35842421Syokota	s = spltty();
35942421Syokota	index = kbd_find_keyboard(driver, unit);
36042421Syokota	if (index >= 0) {
36142421Syokota		if (KBD_IS_BUSY(keyboard[index])) {
36242421Syokota			splx(s);
363127752Sdes			return (-1);
36442421Syokota		}
36542421Syokota		keyboard[index]->kb_token = id;
36642421Syokota		KBD_BUSY(keyboard[index]);
36742421Syokota		keyboard[index]->kb_callback.kc_func = func;
36842421Syokota		keyboard[index]->kb_callback.kc_arg = arg;
369174984Swkoszek		kbdd_clear_state(keyboard[index]);
37042421Syokota	}
37142421Syokota	splx(s);
372127752Sdes	return (index);
37342421Syokota}
37442421Syokota
37542421Syokotaint
37642421Syokotakbd_release(keyboard_t *kbd, void *id)
37742421Syokota{
37842421Syokota	int error;
37942421Syokota	int s;
38042421Syokota
38142421Syokota	s = spltty();
38242421Syokota	if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
38342421Syokota		error = EINVAL;
38442421Syokota	} else if (kbd->kb_token != id) {
38542421Syokota		error = EPERM;
38642421Syokota	} else {
38742421Syokota		kbd->kb_token = NULL;
38842421Syokota		KBD_UNBUSY(kbd);
38942421Syokota		kbd->kb_callback.kc_func = NULL;
39042421Syokota		kbd->kb_callback.kc_arg = NULL;
391174984Swkoszek		kbdd_clear_state(kbd);
39242421Syokota		error = 0;
39342421Syokota	}
39442421Syokota	splx(s);
395127752Sdes	return (error);
39642421Syokota}
39742421Syokota
39842421Syokotaint
39942421Syokotakbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func,
40042421Syokota		    void *arg)
40142421Syokota{
40242421Syokota	int error;
40342421Syokota	int s;
40442421Syokota
40542421Syokota	s = spltty();
40642421Syokota	if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
40742421Syokota		error = EINVAL;
40842421Syokota	} else if (kbd->kb_token != id) {
40942421Syokota		error = EPERM;
41042421Syokota	} else if (func == NULL) {
41142421Syokota		error = EINVAL;
41242421Syokota	} else {
41342421Syokota		kbd->kb_callback.kc_func = func;
41442421Syokota		kbd->kb_callback.kc_arg = arg;
41542421Syokota		error = 0;
41642421Syokota	}
41742421Syokota	splx(s);
418127752Sdes	return (error);
41942421Syokota}
42042421Syokota
42142421Syokota/* get a keyboard structure */
42242421Syokotakeyboard_t
42342421Syokota*kbd_get_keyboard(int index)
42442421Syokota{
42542421Syokota	if ((index < 0) || (index >= keyboards))
426127752Sdes		return (NULL);
42750154Syokota	if (keyboard[index] == NULL)
428127752Sdes		return (NULL);
42942421Syokota	if (!KBD_IS_VALID(keyboard[index]))
430127752Sdes		return (NULL);
431127752Sdes	return (keyboard[index]);
43242421Syokota}
43342421Syokota
43442421Syokota/*
43542421Syokota * The back door for the console driver; configure keyboards
43642421Syokota * This function is for the kernel console to initialize keyboards
43742421Syokota * at very early stage.
43842421Syokota */
43942421Syokota
44042421Syokotaint
44142421Syokotakbd_configure(int flags)
44242421Syokota{
44347295Syokota	const keyboard_driver_t **list;
44447295Syokota	const keyboard_driver_t *p;
44542421Syokota
44654545Syokota	SLIST_FOREACH(p, &keyboard_drivers, link) {
44754545Syokota		if (p->configure != NULL)
44854545Syokota			(*p->configure)(flags);
44954545Syokota	}
45078161Speter	SET_FOREACH(list, kbddriver_set) {
45178161Speter		p = *list;
45242421Syokota		if (p->configure != NULL)
45342421Syokota			(*p->configure)(flags);
45442421Syokota	}
45542421Syokota
456127752Sdes	return (0);
45742421Syokota}
45842421Syokota
45942421Syokota#ifdef KBD_INSTALL_CDEV
46042421Syokota
46142421Syokota/*
46242421Syokota * Virtual keyboard cdev driver functions
46342421Syokota * The virtual keyboard driver dispatches driver functions to
46442421Syokota * appropriate subdrivers.
46542421Syokota */
46642421Syokota
467183397Sed#define KBD_UNIT(dev)	dev2unit(dev)
46842421Syokota
46950154Syokotastatic d_open_t		genkbdopen;
47050154Syokotastatic d_close_t	genkbdclose;
47150154Syokotastatic d_read_t		genkbdread;
47250154Syokotastatic d_write_t	genkbdwrite;
47350154Syokotastatic d_ioctl_t	genkbdioctl;
47450154Syokotastatic d_poll_t		genkbdpoll;
47542421Syokota
47642421Syokota
47742421Syokotastatic struct cdevsw kbd_cdevsw = {
478126080Sphk	.d_version =	D_VERSION,
479126080Sphk	.d_flags =	D_NEEDGIANT,
480111815Sphk	.d_open =	genkbdopen,
481111815Sphk	.d_close =	genkbdclose,
482111815Sphk	.d_read =	genkbdread,
483111815Sphk	.d_write =	genkbdwrite,
484111815Sphk	.d_ioctl =	genkbdioctl,
485111815Sphk	.d_poll =	genkbdpoll,
486111815Sphk	.d_name =	"kbd",
48742421Syokota};
48842421Syokota
48942421Syokotaint
49050154Syokotakbd_attach(keyboard_t *kbd)
49142421Syokota{
49242421Syokota
49342421Syokota	if (kbd->kb_index >= keyboards)
494127752Sdes		return (EINVAL);
49542421Syokota	if (keyboard[kbd->kb_index] != kbd)
496127752Sdes		return (EINVAL);
49742421Syokota
498127751Sdes	kbd->kb_dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL,
499127751Sdes	    0600, "%s%r", kbd->kb_name, kbd->kb_unit);
500125087Sdes	make_dev_alias(kbd->kb_dev, "kbd%r", kbd->kb_index);
501120502Sphk	kbd->kb_dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF,
502127751Sdes	    M_WAITOK | M_ZERO);
50342421Syokota	printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit);
504127752Sdes	return (0);
50542421Syokota}
50642421Syokota
50742421Syokotaint
50850154Syokotakbd_detach(keyboard_t *kbd)
50942421Syokota{
51054545Syokota
51142421Syokota	if (kbd->kb_index >= keyboards)
512127752Sdes		return (EINVAL);
51342421Syokota	if (keyboard[kbd->kb_index] != kbd)
514127752Sdes		return (EINVAL);
51542421Syokota
516120502Sphk	free(kbd->kb_dev->si_drv1, M_DEVBUF);
517120502Sphk	destroy_dev(kbd->kb_dev);
51854545Syokota
519127752Sdes	return (0);
52042421Syokota}
52142421Syokota
52242421Syokota/*
52342421Syokota * Generic keyboard cdev driver functions
52442421Syokota * Keyboard subdrivers may call these functions to implement common
52542421Syokota * driver functions.
52642421Syokota */
52742421Syokota
528193512Sedstatic void
529193512Sedgenkbd_putc(genkbd_softc_t *sc, char c)
530193512Sed{
531193512Sed	unsigned int p;
53242421Syokota
533193512Sed	if (sc->gkb_q_length == KB_QSIZE)
534193512Sed		return;
535193512Sed
536193512Sed	p = (sc->gkb_q_start + sc->gkb_q_length) % KB_QSIZE;
537193512Sed	sc->gkb_q[p] = c;
538193512Sed	sc->gkb_q_length++;
539193512Sed}
540193512Sed
541193512Sedstatic size_t
542193512Sedgenkbd_getc(genkbd_softc_t *sc, char *buf, size_t len)
543193512Sed{
544193512Sed
545193512Sed	/* Determine copy size. */
546193512Sed	if (sc->gkb_q_length == 0)
547193512Sed		return (0);
548193512Sed	if (len >= sc->gkb_q_length)
549193512Sed		len = sc->gkb_q_length;
550193512Sed	if (len >= KB_QSIZE - sc->gkb_q_start)
551193512Sed		len = KB_QSIZE - sc->gkb_q_start;
552193512Sed
553193512Sed	/* Copy out data and progress offset. */
554193512Sed	memcpy(buf, sc->gkb_q + sc->gkb_q_start, len);
555193512Sed	sc->gkb_q_start = (sc->gkb_q_start + len) % KB_QSIZE;
556193512Sed	sc->gkb_q_length -= len;
557193512Sed
558193512Sed	return (len);
559193512Sed}
560193512Sed
56142421Syokotastatic kbd_callback_func_t genkbd_event;
56242421Syokota
56350154Syokotastatic int
564130585Sphkgenkbdopen(struct cdev *dev, int mode, int flag, struct thread *td)
56542421Syokota{
56650154Syokota	keyboard_t *kbd;
56750154Syokota	genkbd_softc_t *sc;
56842421Syokota	int s;
56942421Syokota	int i;
57042421Syokota
57142421Syokota	s = spltty();
57250154Syokota	sc = dev->si_drv1;
57350154Syokota	kbd = kbd_get_keyboard(KBD_INDEX(dev));
57450154Syokota	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
57542421Syokota		splx(s);
576127752Sdes		return (ENXIO);
57742421Syokota	}
57842421Syokota	i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc,
579127751Sdes	    genkbd_event, (void *)sc);
58042421Syokota	if (i < 0) {
58142421Syokota		splx(s);
582127752Sdes		return (EBUSY);
58342421Syokota	}
58442421Syokota	/* assert(i == kbd->kb_index) */
58542421Syokota	/* assert(kbd == kbd_get_keyboard(i)) */
58642421Syokota
58742421Syokota	/*
58842421Syokota	 * NOTE: even when we have successfully claimed a keyboard,
58942421Syokota	 * the device may still be missing (!KBD_HAS_DEVICE(kbd)).
59042421Syokota	 */
59142421Syokota
592193512Sed	sc->gkb_q_length = 0;
59342421Syokota	splx(s);
59442421Syokota
595127752Sdes	return (0);
59642421Syokota}
59742421Syokota
59850154Syokotastatic int
599130585Sphkgenkbdclose(struct cdev *dev, int mode, int flag, struct thread *td)
60042421Syokota{
60150154Syokota	keyboard_t *kbd;
60250154Syokota	genkbd_softc_t *sc;
60342421Syokota	int s;
60442421Syokota
60542421Syokota	/*
60642421Syokota	 * NOTE: the device may have already become invalid.
60750154Syokota	 * kbd == NULL || !KBD_IS_VALID(kbd)
60842421Syokota	 */
60942421Syokota	s = spltty();
61050154Syokota	sc = dev->si_drv1;
61150154Syokota	kbd = kbd_get_keyboard(KBD_INDEX(dev));
61250154Syokota	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
61350154Syokota		/* XXX: we shall be forgiving and don't report error... */
61450154Syokota	} else {
61550154Syokota		kbd_release(kbd, (void *)sc);
61650154Syokota	}
61742421Syokota	splx(s);
618127752Sdes	return (0);
61942421Syokota}
62042421Syokota
62150154Syokotastatic int
622130585Sphkgenkbdread(struct cdev *dev, struct uio *uio, int flag)
62342421Syokota{
62450154Syokota	keyboard_t *kbd;
62550154Syokota	genkbd_softc_t *sc;
62642421Syokota	u_char buffer[KB_BUFSIZE];
62742421Syokota	int len;
62842421Syokota	int error;
62942421Syokota	int s;
63042421Syokota
63142421Syokota	/* wait for input */
63242421Syokota	s = spltty();
63350154Syokota	sc = dev->si_drv1;
63450154Syokota	kbd = kbd_get_keyboard(KBD_INDEX(dev));
63550154Syokota	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
63650154Syokota		splx(s);
637127752Sdes		return (ENXIO);
63850154Syokota	}
639193512Sed	while (sc->gkb_q_length == 0) {
640139193Sphk		if (flag & O_NONBLOCK) {
64142421Syokota			splx(s);
642127752Sdes			return (EWOULDBLOCK);
64342421Syokota		}
64442421Syokota		sc->gkb_flags |= KB_ASLEEP;
645111748Sdes		error = tsleep(sc, PZERO | PCATCH, "kbdrea", 0);
64650154Syokota		kbd = kbd_get_keyboard(KBD_INDEX(dev));
64750154Syokota		if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
64850154Syokota			splx(s);
649127752Sdes			return (ENXIO);	/* our keyboard has gone... */
65050154Syokota		}
65142421Syokota		if (error) {
65242421Syokota			sc->gkb_flags &= ~KB_ASLEEP;
65342421Syokota			splx(s);
654127752Sdes			return (error);
65542421Syokota		}
65642421Syokota	}
65742421Syokota	splx(s);
65842421Syokota
65942421Syokota	/* copy as much input as possible */
66042421Syokota	error = 0;
66142421Syokota	while (uio->uio_resid > 0) {
66242421Syokota		len = imin(uio->uio_resid, sizeof(buffer));
663193512Sed		len = genkbd_getc(sc, buffer, len);
66442421Syokota		if (len <= 0)
66542421Syokota			break;
66642421Syokota		error = uiomove(buffer, len, uio);
66742421Syokota		if (error)
66842421Syokota			break;
66942421Syokota	}
67042421Syokota
671127752Sdes	return (error);
67242421Syokota}
67342421Syokota
67450154Syokotastatic int
675130585Sphkgenkbdwrite(struct cdev *dev, struct uio *uio, int flag)
67642421Syokota{
67750154Syokota	keyboard_t *kbd;
67850154Syokota
67950154Syokota	kbd = kbd_get_keyboard(KBD_INDEX(dev));
68050154Syokota	if ((kbd == NULL) || !KBD_IS_VALID(kbd))
681127752Sdes		return (ENXIO);
682127752Sdes	return (ENODEV);
68342421Syokota}
68442421Syokota
68550154Syokotastatic int
686130585Sphkgenkbdioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
68742421Syokota{
68850154Syokota	keyboard_t *kbd;
68942421Syokota	int error;
69042421Syokota
69150154Syokota	kbd = kbd_get_keyboard(KBD_INDEX(dev));
69250154Syokota	if ((kbd == NULL) || !KBD_IS_VALID(kbd))
693127752Sdes		return (ENXIO);
694174984Swkoszek	error = kbdd_ioctl(kbd, cmd, arg);
69542421Syokota	if (error == ENOIOCTL)
69642421Syokota		error = ENODEV;
697127752Sdes	return (error);
69842421Syokota}
69942421Syokota
70050154Syokotastatic int
701130585Sphkgenkbdpoll(struct cdev *dev, int events, struct thread *td)
70242421Syokota{
70350154Syokota	keyboard_t *kbd;
70450154Syokota	genkbd_softc_t *sc;
70542421Syokota	int revents;
70642421Syokota	int s;
70742421Syokota
70842421Syokota	revents = 0;
70942421Syokota	s = spltty();
71050154Syokota	sc = dev->si_drv1;
71150154Syokota	kbd = kbd_get_keyboard(KBD_INDEX(dev));
71250154Syokota	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
71350154Syokota		revents =  POLLHUP;	/* the keyboard has gone */
71450154Syokota	} else if (events & (POLLIN | POLLRDNORM)) {
715193512Sed		if (sc->gkb_q_length > 0)
71650154Syokota			revents = events & (POLLIN | POLLRDNORM);
71742421Syokota		else
71883366Sjulian			selrecord(td, &sc->gkb_rsel);
71942421Syokota	}
72042421Syokota	splx(s);
721127752Sdes	return (revents);
72242421Syokota}
72342421Syokota
72442421Syokotastatic int
72542421Syokotagenkbd_event(keyboard_t *kbd, int event, void *arg)
72642421Syokota{
72742421Syokota	genkbd_softc_t *sc;
72842421Syokota	size_t len;
72942421Syokota	u_char *cp;
73042421Syokota	int mode;
731197400Sed	u_int c;
73242421Syokota
73342421Syokota	/* assert(KBD_IS_VALID(kbd)) */
73442421Syokota	sc = (genkbd_softc_t *)arg;
73542421Syokota
73642421Syokota	switch (event) {
73742421Syokota	case KBDIO_KEYINPUT:
73842421Syokota		break;
73942421Syokota	case KBDIO_UNLOADING:
74042421Syokota		/* the keyboard is going... */
74142421Syokota		kbd_release(kbd, (void *)sc);
74250154Syokota		if (sc->gkb_flags & KB_ASLEEP) {
74350154Syokota			sc->gkb_flags &= ~KB_ASLEEP;
744111748Sdes			wakeup(sc);
74550154Syokota		}
746122352Stanimura		selwakeuppri(&sc->gkb_rsel, PZERO);
747127752Sdes		return (0);
74842421Syokota	default:
749127752Sdes		return (EINVAL);
75042421Syokota	}
75142421Syokota
75242421Syokota	/* obtain the current key input mode */
753174984Swkoszek	if (kbdd_ioctl(kbd, KDGKBMODE, (caddr_t)&mode))
75442421Syokota		mode = K_XLATE;
75542421Syokota
75642421Syokota	/* read all pending input */
757174984Swkoszek	while (kbdd_check_char(kbd)) {
758174984Swkoszek		c = kbdd_read_char(kbd, FALSE);
75942421Syokota		if (c == NOKEY)
76042421Syokota			continue;
76142421Syokota		if (c == ERRKEY)	/* XXX: ring bell? */
76242421Syokota			continue;
76342421Syokota		if (!KBD_IS_BUSY(kbd))
76442421Syokota			/* the device is not open, discard the input */
76542421Syokota			continue;
76642421Syokota
76742421Syokota		/* store the byte as is for K_RAW and K_CODE modes */
76842421Syokota		if (mode != K_XLATE) {
769193512Sed			genkbd_putc(sc, KEYCHAR(c));
77042421Syokota			continue;
77142421Syokota		}
77242421Syokota
77342421Syokota		/* K_XLATE */
77442421Syokota		if (c & RELKEY)	/* key release is ignored */
77542421Syokota			continue;
77642421Syokota
77742421Syokota		/* process special keys; most of them are just ignored... */
77842421Syokota		if (c & SPCLKEY) {
77942421Syokota			switch (KEYCHAR(c)) {
78054382Syokota			default:
78142421Syokota				/* ignore them... */
78242421Syokota				continue;
78342421Syokota			case BTAB:	/* a backtab: ESC [ Z */
784193512Sed				genkbd_putc(sc, 0x1b);
785193512Sed				genkbd_putc(sc, '[');
786193512Sed				genkbd_putc(sc, 'Z');
78742421Syokota				continue;
78842421Syokota			}
78942421Syokota		}
79042421Syokota
79142421Syokota		/* normal chars, normal chars with the META, function keys */
79242421Syokota		switch (KEYFLAGS(c)) {
79342421Syokota		case 0:			/* a normal char */
794193512Sed			genkbd_putc(sc, KEYCHAR(c));
79542421Syokota			break;
79642421Syokota		case MKEY:		/* the META flag: prepend ESC */
797193512Sed			genkbd_putc(sc, 0x1b);
798193512Sed			genkbd_putc(sc, KEYCHAR(c));
79942421Syokota			break;
80042421Syokota		case FKEY | SPCLKEY:	/* a function key, return string */
801174984Swkoszek			cp = kbdd_get_fkeystr(kbd, KEYCHAR(c), &len);
80242421Syokota			if (cp != NULL) {
80342421Syokota				while (len-- >  0)
804193512Sed					genkbd_putc(sc, *cp++);
80542421Syokota			}
80642421Syokota			break;
80742421Syokota		}
80842421Syokota	}
80942421Syokota
81042421Syokota	/* wake up sleeping/polling processes */
811193512Sed	if (sc->gkb_q_length > 0) {
81242421Syokota		if (sc->gkb_flags & KB_ASLEEP) {
81342421Syokota			sc->gkb_flags &= ~KB_ASLEEP;
814111748Sdes			wakeup(sc);
81542421Syokota		}
816122352Stanimura		selwakeuppri(&sc->gkb_rsel, PZERO);
81742421Syokota	}
81842421Syokota
819127752Sdes	return (0);
82042421Syokota}
82142421Syokota
82242421Syokota#endif /* KBD_INSTALL_CDEV */
82342421Syokota
82442421Syokota/*
82542421Syokota * Generic low-level keyboard functions
82642421Syokota * The low-level functions in the keyboard subdriver may use these
82742421Syokota * functions.
82842421Syokota */
82942421Syokota
830112050Sdwmalone#ifndef KBD_DISABLE_KEYMAP_LOAD
831112050Sdwmalonestatic int key_change_ok(struct keyent_t *, struct keyent_t *, struct thread *);
832112050Sdwmalonestatic int keymap_change_ok(keymap_t *, keymap_t *, struct thread *);
833112050Sdwmalonestatic int accent_change_ok(accentmap_t *, accentmap_t *, struct thread *);
834112050Sdwmalonestatic int fkey_change_ok(fkeytab_t *, fkeyarg_t *, struct thread *);
835112050Sdwmalone#endif
836112050Sdwmalone
83742421Syokotaint
83842421Syokotagenkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
83942421Syokota{
840197330Sed	keymap_t *mapp;
841224126Sed	okeymap_t *omapp;
84242421Syokota	keyarg_t *keyp;
84342421Syokota	fkeyarg_t *fkeyp;
84442421Syokota	int s;
845224126Sed	int i, j;
846112050Sdwmalone	int error;
84742421Syokota
84842421Syokota	s = spltty();
84942421Syokota	switch (cmd) {
85042421Syokota
85142421Syokota	case KDGKBINFO:		/* get keyboard information */
85242421Syokota		((keyboard_info_t *)arg)->kb_index = kbd->kb_index;
85342421Syokota		i = imin(strlen(kbd->kb_name) + 1,
854127751Sdes		    sizeof(((keyboard_info_t *)arg)->kb_name));
85542421Syokota		bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i);
85642421Syokota		((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit;
85742421Syokota		((keyboard_info_t *)arg)->kb_type = kbd->kb_type;
85842421Syokota		((keyboard_info_t *)arg)->kb_config = kbd->kb_config;
85942421Syokota		((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags;
86042421Syokota		break;
86142421Syokota
86242421Syokota	case KDGKBTYPE:		/* get keyboard type */
86342421Syokota		*(int *)arg = kbd->kb_type;
86442421Syokota		break;
86542421Syokota
86654543Syokota	case KDGETREPEAT:	/* get keyboard repeat rate */
86754543Syokota		((int *)arg)[0] = kbd->kb_delay1;
868127751Sdes		((int *)arg)[1] = kbd->kb_delay2;
86954543Syokota		break;
87054543Syokota
87142421Syokota	case GIO_KEYMAP:	/* get keyboard translation table */
872197330Sed		error = copyout(kbd->kb_keymap, *(void **)arg,
873197330Sed		    sizeof(keymap_t));
874197330Sed		splx(s);
875197330Sed		return (error);
876224126Sed	case OGIO_KEYMAP:	/* get keyboard translation table (compat) */
877224126Sed		mapp = kbd->kb_keymap;
878224126Sed		omapp = (okeymap_t *)arg;
879224126Sed		omapp->n_keys = mapp->n_keys;
880224126Sed		for (i = 0; i < NUM_KEYS; i++) {
881224126Sed			for (j = 0; j < NUM_STATES; j++)
882224126Sed				omapp->key[i].map[j] =
883224126Sed				    mapp->key[i].map[j];
884224126Sed			omapp->key[i].spcl = mapp->key[i].spcl;
885224126Sed			omapp->key[i].flgs = mapp->key[i].flgs;
886224126Sed		}
887224126Sed		return (0);
88842421Syokota	case PIO_KEYMAP:	/* set keyboard translation table */
889224126Sed	case OPIO_KEYMAP:	/* set keyboard translation table (compat) */
89044628Syokota#ifndef KBD_DISABLE_KEYMAP_LOAD
891197330Sed		mapp = malloc(sizeof *mapp, M_TEMP, M_NOWAIT);
892224126Sed		if (cmd == OPIO_KEYMAP) {
893224126Sed			omapp = (okeymap_t *)arg;
894224126Sed			mapp->n_keys = omapp->n_keys;
895224126Sed			for (i = 0; i < NUM_KEYS; i++) {
896224126Sed				for (j = 0; j < NUM_STATES; j++)
897224126Sed					mapp->key[i].map[j] =
898224126Sed					    omapp->key[i].map[j];
899224126Sed				mapp->key[i].spcl = omapp->key[i].spcl;
900224126Sed				mapp->key[i].flgs = omapp->key[i].flgs;
901224126Sed			}
902224126Sed		} else {
903224126Sed			error = copyin(*(void **)arg, mapp, sizeof *mapp);
904224126Sed			if (error != 0) {
905224126Sed				splx(s);
906224126Sed				free(mapp, M_TEMP);
907224126Sed				return (error);
908224126Sed			}
909112050Sdwmalone		}
910197330Sed
911197330Sed		error = keymap_change_ok(kbd->kb_keymap, mapp, curthread);
912197330Sed		if (error != 0) {
913197330Sed			splx(s);
914197330Sed			free(mapp, M_TEMP);
915197330Sed			return (error);
916197330Sed		}
91742421Syokota		bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
918197330Sed		bcopy(mapp, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
919197330Sed		free(mapp, M_TEMP);
92042421Syokota		break;
92144628Syokota#else
92244628Syokota		splx(s);
923127752Sdes		return (ENODEV);
92444628Syokota#endif
92542421Syokota
92642421Syokota	case GIO_KEYMAPENT:	/* get keyboard translation table entry */
92742421Syokota		keyp = (keyarg_t *)arg;
928127751Sdes		if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /
929127751Sdes		    sizeof(kbd->kb_keymap->key[0])) {
93042421Syokota			splx(s);
931127752Sdes			return (EINVAL);
93242421Syokota		}
93342573Syokota		bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key,
934127751Sdes		    sizeof(keyp->key));
93542421Syokota		break;
93642421Syokota	case PIO_KEYMAPENT:	/* set keyboard translation table entry */
93744628Syokota#ifndef KBD_DISABLE_KEYMAP_LOAD
93842421Syokota		keyp = (keyarg_t *)arg;
939127751Sdes		if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /
940127751Sdes		    sizeof(kbd->kb_keymap->key[0])) {
94142421Syokota			splx(s);
942127752Sdes			return (EINVAL);
94342421Syokota		}
944112050Sdwmalone		error = key_change_ok(&kbd->kb_keymap->key[keyp->keynum],
945112050Sdwmalone		    &keyp->key, curthread);
946112050Sdwmalone		if (error != 0) {
947112050Sdwmalone			splx(s);
948127752Sdes			return (error);
949112050Sdwmalone		}
95042573Syokota		bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
951127751Sdes		    sizeof(keyp->key));
95242421Syokota		break;
95344628Syokota#else
95444628Syokota		splx(s);
955127752Sdes		return (ENODEV);
95644628Syokota#endif
95742421Syokota
95842421Syokota	case GIO_DEADKEYMAP:	/* get accent key translation table */
95942421Syokota		bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap));
96042421Syokota		break;
96142421Syokota	case PIO_DEADKEYMAP:	/* set accent key translation table */
96244628Syokota#ifndef KBD_DISABLE_KEYMAP_LOAD
963112050Sdwmalone		error = accent_change_ok(kbd->kb_accentmap,
964112050Sdwmalone		    (accentmap_t *)arg, curthread);
965112050Sdwmalone		if (error != 0) {
966112050Sdwmalone			splx(s);
967127752Sdes			return (error);
968112050Sdwmalone		}
96942421Syokota		bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
97042421Syokota		break;
97144628Syokota#else
97244628Syokota		splx(s);
973127752Sdes		return (ENODEV);
97444628Syokota#endif
97542421Syokota
97642421Syokota	case GETFKEY:		/* get functionkey string */
97742421Syokota		fkeyp = (fkeyarg_t *)arg;
97842421Syokota		if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
97942421Syokota			splx(s);
980127752Sdes			return (EINVAL);
98142421Syokota		}
98242421Syokota		bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef,
983127751Sdes		    kbd->kb_fkeytab[fkeyp->keynum].len);
98442421Syokota		fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len;
98542421Syokota		break;
98642421Syokota	case SETFKEY:		/* set functionkey string */
98744628Syokota#ifndef KBD_DISABLE_KEYMAP_LOAD
98842421Syokota		fkeyp = (fkeyarg_t *)arg;
98942421Syokota		if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
99042421Syokota			splx(s);
991127752Sdes			return (EINVAL);
99242421Syokota		}
993112050Sdwmalone		error = fkey_change_ok(&kbd->kb_fkeytab[fkeyp->keynum],
994112050Sdwmalone		    fkeyp, curthread);
995112050Sdwmalone		if (error != 0) {
996112050Sdwmalone			splx(s);
997127752Sdes			return (error);
998112050Sdwmalone		}
999300088Sglebius		kbd->kb_fkeytab[fkeyp->keynum].len = min(fkeyp->flen, MAXFK);
100042421Syokota		bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
1001127751Sdes		    kbd->kb_fkeytab[fkeyp->keynum].len);
100242421Syokota		break;
100344628Syokota#else
100444628Syokota		splx(s);
1005127752Sdes		return (ENODEV);
100644628Syokota#endif
100742421Syokota
100842421Syokota	default:
100942421Syokota		splx(s);
1010127752Sdes		return (ENOIOCTL);
101142421Syokota	}
101242421Syokota
101342421Syokota	splx(s);
1014127752Sdes	return (0);
101542421Syokota}
101642421Syokota
1017112050Sdwmalone#ifndef KBD_DISABLE_KEYMAP_LOAD
1018112050Sdwmalone#define RESTRICTED_KEY(key, i) \
1019112050Sdwmalone	((key->spcl & (0x80 >> i)) && \
1020112050Sdwmalone		(key->map[i] == RBT || key->map[i] == SUSP || \
1021112050Sdwmalone		 key->map[i] == STBY || key->map[i] == DBG || \
1022112050Sdwmalone		 key->map[i] == PNC || key->map[i] == HALT || \
1023112050Sdwmalone		 key->map[i] == PDWN))
1024112050Sdwmalone
1025112050Sdwmalonestatic int
1026112050Sdwmalonekey_change_ok(struct keyent_t *oldkey, struct keyent_t *newkey, struct thread *td)
1027112050Sdwmalone{
1028112050Sdwmalone	int i;
1029112050Sdwmalone
1030112050Sdwmalone	/* Low keymap_restrict_change means any changes are OK. */
1031112050Sdwmalone	if (keymap_restrict_change <= 0)
1032127752Sdes		return (0);
1033112050Sdwmalone
1034112050Sdwmalone	/* High keymap_restrict_change means only root can change the keymap. */
1035112050Sdwmalone	if (keymap_restrict_change >= 2) {
1036112050Sdwmalone		for (i = 0; i < NUM_STATES; i++)
1037112050Sdwmalone			if (oldkey->map[i] != newkey->map[i])
1038164033Srwatson				return priv_check(td, PRIV_KEYBOARD);
1039112050Sdwmalone		if (oldkey->spcl != newkey->spcl)
1040164033Srwatson			return priv_check(td, PRIV_KEYBOARD);
1041112050Sdwmalone		if (oldkey->flgs != newkey->flgs)
1042164033Srwatson			return priv_check(td, PRIV_KEYBOARD);
1043127752Sdes		return (0);
1044112050Sdwmalone	}
1045112050Sdwmalone
1046112050Sdwmalone	/* Otherwise we have to see if any special keys are being changed. */
1047112050Sdwmalone	for (i = 0; i < NUM_STATES; i++) {
1048112050Sdwmalone		/*
1049112050Sdwmalone		 * If either the oldkey or the newkey action is restricted
1050112050Sdwmalone		 * then we must make sure that the action doesn't change.
1051112050Sdwmalone		 */
1052112050Sdwmalone		if (!RESTRICTED_KEY(oldkey, i) && !RESTRICTED_KEY(newkey, i))
1053112050Sdwmalone			continue;
1054112050Sdwmalone		if ((oldkey->spcl & (0x80 >> i)) == (newkey->spcl & (0x80 >> i))
1055112050Sdwmalone		    && oldkey->map[i] == newkey->map[i])
1056112050Sdwmalone			continue;
1057164033Srwatson		return priv_check(td, PRIV_KEYBOARD);
1058112050Sdwmalone	}
1059112050Sdwmalone
1060127752Sdes	return (0);
1061112050Sdwmalone}
1062112050Sdwmalone
1063112050Sdwmalonestatic int
1064112050Sdwmalonekeymap_change_ok(keymap_t *oldmap, keymap_t *newmap, struct thread *td)
1065112050Sdwmalone{
1066112050Sdwmalone	int keycode, error;
1067112050Sdwmalone
1068112050Sdwmalone	for (keycode = 0; keycode < NUM_KEYS; keycode++) {
1069112050Sdwmalone		if ((error = key_change_ok(&oldmap->key[keycode],
1070112050Sdwmalone		    &newmap->key[keycode], td)) != 0)
1071127752Sdes			return (error);
1072112050Sdwmalone	}
1073127752Sdes	return (0);
1074112050Sdwmalone}
1075112050Sdwmalone
1076112050Sdwmalonestatic int
1077112050Sdwmaloneaccent_change_ok(accentmap_t *oldmap, accentmap_t *newmap, struct thread *td)
1078112050Sdwmalone{
1079112050Sdwmalone	struct acc_t *oldacc, *newacc;
1080112050Sdwmalone	int accent, i;
1081112050Sdwmalone
1082112050Sdwmalone	if (keymap_restrict_change <= 2)
1083127752Sdes		return (0);
1084112050Sdwmalone
1085112050Sdwmalone	if (oldmap->n_accs != newmap->n_accs)
1086164033Srwatson		return priv_check(td, PRIV_KEYBOARD);
1087112050Sdwmalone
1088112050Sdwmalone	for (accent = 0; accent < oldmap->n_accs; accent++) {
1089112050Sdwmalone		oldacc = &oldmap->acc[accent];
1090112050Sdwmalone		newacc = &newmap->acc[accent];
1091112050Sdwmalone		if (oldacc->accchar != newacc->accchar)
1092164033Srwatson			return priv_check(td, PRIV_KEYBOARD);
1093112050Sdwmalone		for (i = 0; i < NUM_ACCENTCHARS; ++i) {
1094112050Sdwmalone			if (oldacc->map[i][0] != newacc->map[i][0])
1095164033Srwatson				return priv_check(td, PRIV_KEYBOARD);
1096112050Sdwmalone			if (oldacc->map[i][0] == 0)	/* end of table */
1097112050Sdwmalone				break;
1098112050Sdwmalone			if (oldacc->map[i][1] != newacc->map[i][1])
1099164033Srwatson				return priv_check(td, PRIV_KEYBOARD);
1100112050Sdwmalone		}
1101112050Sdwmalone	}
1102112050Sdwmalone
1103127752Sdes	return (0);
1104112050Sdwmalone}
1105112050Sdwmalone
1106112050Sdwmalonestatic int
1107112050Sdwmalonefkey_change_ok(fkeytab_t *oldkey, fkeyarg_t *newkey, struct thread *td)
1108112050Sdwmalone{
1109112050Sdwmalone	if (keymap_restrict_change <= 3)
1110127752Sdes		return (0);
1111112050Sdwmalone
1112112050Sdwmalone	if (oldkey->len != newkey->flen ||
1113112050Sdwmalone	    bcmp(oldkey->str, newkey->keydef, oldkey->len) != 0)
1114164033Srwatson		return priv_check(td, PRIV_KEYBOARD);
1115112050Sdwmalone
1116127752Sdes	return (0);
1117112050Sdwmalone}
1118112050Sdwmalone#endif
1119112050Sdwmalone
112042421Syokota/* get a pointer to the string associated with the given function key */
112142421Syokotau_char
112242421Syokota*genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
112342421Syokota{
112442421Syokota	if (kbd == NULL)
1125127752Sdes		return (NULL);
112642421Syokota	fkey -= F_FN;
112742421Syokota	if (fkey > kbd->kb_fkeytab_size)
1128127752Sdes		return (NULL);
112942421Syokota	*len = kbd->kb_fkeytab[fkey].len;
1130127752Sdes	return (kbd->kb_fkeytab[fkey].str);
113142421Syokota}
113242421Syokota
113342421Syokota/* diagnostic dump */
113442421Syokotastatic char
113542421Syokota*get_kbd_type_name(int type)
113642421Syokota{
113742421Syokota	static struct {
113842421Syokota		int type;
113942421Syokota		char *name;
114042421Syokota	} name_table[] = {
114142421Syokota		{ KB_84,	"AT 84" },
114242421Syokota		{ KB_101,	"AT 101/102" },
114342421Syokota		{ KB_OTHER,	"generic" },
114442421Syokota	};
114542421Syokota	int i;
114642421Syokota
114742421Syokota	for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) {
114842421Syokota		if (type == name_table[i].type)
1149127752Sdes			return (name_table[i].name);
115042421Syokota	}
1151127752Sdes	return ("unknown");
115242421Syokota}
115342421Syokota
115442421Syokotavoid
115542421Syokotagenkbd_diag(keyboard_t *kbd, int level)
115642421Syokota{
115742421Syokota	if (level > 0) {
1158127751Sdes		printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x",
1159127751Sdes		    kbd->kb_index, kbd->kb_name, kbd->kb_unit,
1160127751Sdes		    get_kbd_type_name(kbd->kb_type), kbd->kb_type,
1161127751Sdes		    kbd->kb_config, kbd->kb_flags);
116242421Syokota		if (kbd->kb_io_base > 0)
1163127751Sdes			printf(", port:0x%x-0x%x", kbd->kb_io_base,
1164127751Sdes			    kbd->kb_io_base + kbd->kb_io_size - 1);
116542421Syokota		printf("\n");
116642421Syokota	}
116742421Syokota}
116842421Syokota
116942421Syokota#define set_lockkey_state(k, s, l)				\
117042421Syokota	if (!((s) & l ## DOWN)) {				\
117142421Syokota		int i;						\
117242421Syokota		(s) |= l ## DOWN;				\
117342421Syokota		(s) ^= l ## ED;					\
117442421Syokota		i = (s) & LOCK_MASK;				\
1175213770Srpaulo		(void)kbdd_ioctl((k), KDSETLED, (caddr_t)&i);	\
117642421Syokota	}
117742421Syokota
117842421Syokotastatic u_int
117942421Syokotasave_accent_key(keyboard_t *kbd, u_int key, int *accents)
118042421Syokota{
118142421Syokota	int i;
118242421Syokota
118342421Syokota	/* make an index into the accent map */
118442421Syokota	i = key - F_ACC + 1;
118542421Syokota	if ((i > kbd->kb_accentmap->n_accs)
118642421Syokota	    || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) {
118742421Syokota		/* the index is out of range or pointing to an empty entry */
118842421Syokota		*accents = 0;
1189127752Sdes		return (ERRKEY);
119042421Syokota	}
119142421Syokota
1192127751Sdes	/*
1193127751Sdes	 * If the same accent key has been hit twice, produce the accent
1194127751Sdes	 * char itself.
119542421Syokota	 */
119642421Syokota	if (i == *accents) {
119742421Syokota		key = kbd->kb_accentmap->acc[i - 1].accchar;
119842421Syokota		*accents = 0;
1199127752Sdes		return (key);
120042421Syokota	}
120142421Syokota
120242421Syokota	/* remember the index and wait for the next key  */
1203127751Sdes	*accents = i;
1204127752Sdes	return (NOKEY);
120542421Syokota}
120642421Syokota
120742421Syokotastatic u_int
120842421Syokotamake_accent_char(keyboard_t *kbd, u_int ch, int *accents)
120942421Syokota{
121042421Syokota	struct acc_t *acc;
121142421Syokota	int i;
121242421Syokota
121342421Syokota	acc = &kbd->kb_accentmap->acc[*accents - 1];
121442421Syokota	*accents = 0;
121542421Syokota
1216127751Sdes	/*
121742421Syokota	 * If the accent key is followed by the space key,
121842421Syokota	 * produce the accent char itself.
121942421Syokota	 */
122042421Syokota	if (ch == ' ')
1221127752Sdes		return (acc->accchar);
122242421Syokota
122342421Syokota	/* scan the accent map */
122442421Syokota	for (i = 0; i < NUM_ACCENTCHARS; ++i) {
122542421Syokota		if (acc->map[i][0] == 0)	/* end of table */
122642421Syokota			break;
122742421Syokota		if (acc->map[i][0] == ch)
1228127752Sdes			return (acc->map[i][1]);
122942421Syokota	}
123042421Syokota	/* this char cannot be accented... */
1231127752Sdes	return (ERRKEY);
123242421Syokota}
123342421Syokota
123442421Syokotaint
123542421Syokotagenkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate,
123642421Syokota		 int *accents)
123742421Syokota{
123842421Syokota	struct keyent_t *key;
123942421Syokota	int state = *shiftstate;
124042421Syokota	int action;
124142421Syokota	int f;
124242421Syokota	int i;
124342421Syokota
124454382Syokota	i = keycode;
124542421Syokota	f = state & (AGRS | ALKED);
124642421Syokota	if ((f == AGRS1) || (f == AGRS2) || (f == ALKED))
124754382Syokota		i += ALTGR_OFFSET;
124854382Syokota	key = &kbd->kb_keymap->key[i];
124942421Syokota	i = ((state & SHIFTS) ? 1 : 0)
125042421Syokota	    | ((state & CTLS) ? 2 : 0)
125142421Syokota	    | ((state & ALTS) ? 4 : 0);
125242421Syokota	if (((key->flgs & FLAG_LOCK_C) && (state & CLKED))
125342421Syokota		|| ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) )
125442421Syokota		i ^= 1;
125542421Syokota
125642421Syokota	if (up) {	/* break: key released */
125780040Syokota		action = kbd->kb_lastact[keycode];
125880040Syokota		kbd->kb_lastact[keycode] = NOP;
125980040Syokota		switch (action) {
126080040Syokota		case LSHA:
126180040Syokota			if (state & SHIFTAON) {
126280040Syokota				set_lockkey_state(kbd, state, ALK);
126380040Syokota				state &= ~ALKDOWN;
126480040Syokota			}
126580040Syokota			action = LSH;
126680040Syokota			/* FALL THROUGH */
126780040Syokota		case LSH:
126880040Syokota			state &= ~SHIFTS1;
126980040Syokota			break;
127080040Syokota		case RSHA:
127180040Syokota			if (state & SHIFTAON) {
127280040Syokota				set_lockkey_state(kbd, state, ALK);
127380040Syokota				state &= ~ALKDOWN;
127480040Syokota			}
127580040Syokota			action = RSH;
127680040Syokota			/* FALL THROUGH */
127780040Syokota		case RSH:
127880040Syokota			state &= ~SHIFTS2;
127980040Syokota			break;
128080040Syokota		case LCTRA:
128180040Syokota			if (state & SHIFTAON) {
128280040Syokota				set_lockkey_state(kbd, state, ALK);
128380040Syokota				state &= ~ALKDOWN;
128480040Syokota			}
128580040Syokota			action = LCTR;
128680040Syokota			/* FALL THROUGH */
128780040Syokota		case LCTR:
128880040Syokota			state &= ~CTLS1;
128980040Syokota			break;
129080040Syokota		case RCTRA:
129180040Syokota			if (state & SHIFTAON) {
129280040Syokota				set_lockkey_state(kbd, state, ALK);
129380040Syokota				state &= ~ALKDOWN;
129480040Syokota			}
129580040Syokota			action = RCTR;
129680040Syokota			/* FALL THROUGH */
129780040Syokota		case RCTR:
129880040Syokota			state &= ~CTLS2;
129980040Syokota			break;
130080040Syokota		case LALTA:
130180040Syokota			if (state & SHIFTAON) {
130280040Syokota				set_lockkey_state(kbd, state, ALK);
130380040Syokota				state &= ~ALKDOWN;
130480040Syokota			}
130580040Syokota			action = LALT;
130680040Syokota			/* FALL THROUGH */
130780040Syokota		case LALT:
130880040Syokota			state &= ~ALTS1;
130980040Syokota			break;
131080040Syokota		case RALTA:
131180040Syokota			if (state & SHIFTAON) {
131280040Syokota				set_lockkey_state(kbd, state, ALK);
131380040Syokota				state &= ~ALKDOWN;
131480040Syokota			}
131580040Syokota			action = RALT;
131680040Syokota			/* FALL THROUGH */
131780040Syokota		case RALT:
131880040Syokota			state &= ~ALTS2;
131980040Syokota			break;
132080040Syokota		case ASH:
132180040Syokota			state &= ~AGRS1;
132280040Syokota			break;
132380040Syokota		case META:
132480040Syokota			state &= ~METAS1;
132580040Syokota			break;
132680040Syokota		case NLK:
132780040Syokota			state &= ~NLKDOWN;
132880040Syokota			break;
132980040Syokota		case CLK:
133042421Syokota#ifndef PC98
133180040Syokota			state &= ~CLKDOWN;
133242421Syokota#else
133380040Syokota			state &= ~CLKED;
133480040Syokota			i = state & LOCK_MASK;
1335213770Srpaulo			(void)kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i);
133642421Syokota#endif
133780040Syokota			break;
133880040Syokota		case SLK:
133980040Syokota			state &= ~SLKDOWN;
134080040Syokota			break;
134180040Syokota		case ALK:
134280040Syokota			state &= ~ALKDOWN;
134380040Syokota			break;
134480040Syokota		case NOP:
134580040Syokota			/* release events of regular keys are not reported */
134680040Syokota			*shiftstate &= ~SHIFTAON;
1347127752Sdes			return (NOKEY);
134842421Syokota		}
134980040Syokota		*shiftstate = state & ~SHIFTAON;
135080040Syokota		return (SPCLKEY | RELKEY | action);
135142421Syokota	} else {	/* make: key pressed */
135280040Syokota		action = key->map[i];
135355820Syokota		state &= ~SHIFTAON;
135442421Syokota		if (key->spcl & (0x80 >> i)) {
135542421Syokota			/* special keys */
135680040Syokota			if (kbd->kb_lastact[keycode] == NOP)
135780040Syokota				kbd->kb_lastact[keycode] = action;
135880040Syokota			if (kbd->kb_lastact[keycode] != action)
135980040Syokota				action = NOP;
136042421Syokota			switch (action) {
136142421Syokota			/* LOCKING KEYS */
136242421Syokota			case NLK:
136342421Syokota				set_lockkey_state(kbd, state, NLK);
136442421Syokota				break;
136542421Syokota			case CLK:
136642421Syokota#ifndef PC98
136742421Syokota				set_lockkey_state(kbd, state, CLK);
136842421Syokota#else
136942421Syokota				state |= CLKED;
137042421Syokota				i = state & LOCK_MASK;
1371213770Srpaulo				(void)kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i);
137242421Syokota#endif
137342421Syokota				break;
137442421Syokota			case SLK:
137542421Syokota				set_lockkey_state(kbd, state, SLK);
137642421Syokota				break;
137742421Syokota			case ALK:
137842421Syokota				set_lockkey_state(kbd, state, ALK);
137942421Syokota				break;
138042421Syokota			/* NON-LOCKING KEYS */
138142421Syokota			case SPSC: case RBT:  case SUSP: case STBY:
138254382Syokota			case DBG:  case NEXT: case PREV: case PNC:
138365759Sdwmalone			case HALT: case PDWN:
138442421Syokota				*accents = 0;
138542421Syokota				break;
138642421Syokota			case BTAB:
138742421Syokota				*accents = 0;
138842421Syokota				action |= BKEY;
138942421Syokota				break;
139054382Syokota			case LSHA:
139155820Syokota				state |= SHIFTAON;
139254382Syokota				action = LSH;
139354382Syokota				/* FALL THROUGH */
139442421Syokota			case LSH:
139542421Syokota				state |= SHIFTS1;
139642421Syokota				break;
139754382Syokota			case RSHA:
139855820Syokota				state |= SHIFTAON;
139954382Syokota				action = RSH;
140054382Syokota				/* FALL THROUGH */
140142421Syokota			case RSH:
140242421Syokota				state |= SHIFTS2;
140342421Syokota				break;
140454382Syokota			case LCTRA:
140555820Syokota				state |= SHIFTAON;
140654382Syokota				action = LCTR;
140754382Syokota				/* FALL THROUGH */
140842421Syokota			case LCTR:
140942421Syokota				state |= CTLS1;
141042421Syokota				break;
141154382Syokota			case RCTRA:
141255820Syokota				state |= SHIFTAON;
141354382Syokota				action = RCTR;
141454382Syokota				/* FALL THROUGH */
141542421Syokota			case RCTR:
141642421Syokota				state |= CTLS2;
141742421Syokota				break;
141854382Syokota			case LALTA:
141955820Syokota				state |= SHIFTAON;
142054382Syokota				action = LALT;
142154382Syokota				/* FALL THROUGH */
142242421Syokota			case LALT:
142342421Syokota				state |= ALTS1;
142442421Syokota				break;
142554382Syokota			case RALTA:
142655820Syokota				state |= SHIFTAON;
142754382Syokota				action = RALT;
142854382Syokota				/* FALL THROUGH */
142942421Syokota			case RALT:
143042421Syokota				state |= ALTS2;
143142421Syokota				break;
143242421Syokota			case ASH:
143342421Syokota				state |= AGRS1;
143442421Syokota				break;
143542421Syokota			case META:
143642421Syokota				state |= METAS1;
143742421Syokota				break;
143880040Syokota			case NOP:
143980040Syokota				*shiftstate = state;
1440127752Sdes				return (NOKEY);
144142421Syokota			default:
144242421Syokota				/* is this an accent (dead) key? */
144355820Syokota				*shiftstate = state;
144442421Syokota				if (action >= F_ACC && action <= L_ACC) {
144542421Syokota					action = save_accent_key(kbd, action,
144642421Syokota								 accents);
144742421Syokota					switch (action) {
144842421Syokota					case NOKEY:
144942421Syokota					case ERRKEY:
1450127752Sdes						return (action);
145142421Syokota					default:
145242421Syokota						if (state & METAS)
145342421Syokota							return (action | MKEY);
145442421Syokota						else
1455127752Sdes							return (action);
145642421Syokota					}
145742421Syokota					/* NOT REACHED */
145842421Syokota				}
145942421Syokota				/* other special keys */
146042421Syokota				if (*accents > 0) {
146142421Syokota					*accents = 0;
1462127752Sdes					return (ERRKEY);
146342421Syokota				}
146442421Syokota				if (action >= F_FN && action <= L_FN)
146542421Syokota					action |= FKEY;
146642421Syokota				/* XXX: return fkey string for the FKEY? */
146755820Syokota				return (SPCLKEY | action);
146842421Syokota			}
146942421Syokota			*shiftstate = state;
147042421Syokota			return (SPCLKEY | action);
147142421Syokota		} else {
147242421Syokota			/* regular keys */
147380040Syokota			kbd->kb_lastact[keycode] = NOP;
147455820Syokota			*shiftstate = state;
147542421Syokota			if (*accents > 0) {
147642421Syokota				/* make an accented char */
147742421Syokota				action = make_accent_char(kbd, action, accents);
147842421Syokota				if (action == ERRKEY)
1479127752Sdes					return (action);
148042421Syokota			}
148142421Syokota			if (state & METAS)
148242421Syokota				action |= MKEY;
1483127752Sdes			return (action);
148442421Syokota		}
148542421Syokota	}
148642421Syokota	/* NOT REACHED */
148742421Syokota}
1488