kbd.c revision 213770
1238730Sdelphij/*-
2294286Sdelphij * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3238730Sdelphij * All rights reserved.
4238730Sdelphij *
5238730Sdelphij * Redistribution and use in source and binary forms, with or without
6238730Sdelphij * modification, are permitted provided that the following conditions
7238730Sdelphij * are met:
8238730Sdelphij * 1. Redistributions of source code must retain the above copyright
960786Sps *    notice, this list of conditions and the following disclaimer as
1060786Sps *    the first lines of this file unmodified.
1160786Sps * 2. Redistributions in binary form must reproduce the above copyright
1260786Sps *    notice, this list of conditions and the following disclaimer in the
1360786Sps *    documentation and/or other materials provided with the distribution.
1460786Sps *
1560786Sps * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
1660786Sps * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1760786Sps * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1860786Sps * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1960786Sps * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2060786Sps * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2160786Sps * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2260786Sps * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2360786Sps * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2460786Sps * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2560786Sps *
2660786Sps */
2760786Sps
2860786Sps#include <sys/cdefs.h>
2960786Sps__FBSDID("$FreeBSD: head/sys/dev/kbd/kbd.c 213770 2010-10-13 11:37:12Z rpaulo $");
3060786Sps
3160786Sps#include "opt_kbd.h"
3260786Sps
3360786Sps#include <sys/param.h>
3460786Sps#include <sys/systm.h>
3589019Sps#include <sys/kernel.h>
3689019Sps#include <sys/malloc.h>
3789019Sps#include <sys/conf.h>
3860786Sps#include <sys/fcntl.h>
3960786Sps#include <sys/poll.h>
4060786Sps#include <sys/priv.h>
4160786Sps#include <sys/proc.h>
4260786Sps#include <sys/selinfo.h>
4360786Sps#include <sys/sysctl.h>
4460786Sps#include <sys/uio.h>
4560786Sps
4660786Sps#include <sys/kbio.h>
4760786Sps
4860786Sps#include <dev/kbd/kbdreg.h>
4960786Sps
5060786Sps#define KBD_INDEX(dev)	dev2unit(dev)
5160786Sps
52128345Stjr#define KB_QSIZE	512
53170256Sdelphij#define KB_BUFSIZE	64
54191930Sdelphij
5560786Spstypedef struct genkbd_softc {
5660786Sps	int		gkb_flags;	/* flag/status bits */
5760786Sps#define KB_ASLEEP	(1 << 0)
5860786Sps	struct selinfo	gkb_rsel;
5960786Sps	char		gkb_q[KB_QSIZE];		/* input queue */
6060786Sps	unsigned int	gkb_q_start;
6160786Sps	unsigned int	gkb_q_length;
6260786Sps} genkbd_softc_t;
6360786Sps
6460786Spsstatic	SLIST_HEAD(, keyboard_driver) keyboard_drivers =
6560786Sps	SLIST_HEAD_INITIALIZER(keyboard_drivers);
66128345Stjr
6760786SpsSET_DECLARE(kbddriver_set, const keyboard_driver_t);
6860786Sps
6960786Sps/* local arrays */
7060786Sps
7160786Sps/*
72128345Stjr * We need at least one entry each in order to initialize a keyboard
73128345Stjr * for the kernel console.  The arrays will be increased dynamically
74128345Stjr * when necessary.
75128345Stjr */
76128345Stjr
77128345Stjrstatic int		keyboards = 1;
78128345Stjrstatic keyboard_t	*kbd_ini;
79128345Stjrstatic keyboard_t	**keyboard = &kbd_ini;
80128345Stjrstatic keyboard_switch_t *kbdsw_ini;
81128345Stjr       keyboard_switch_t **kbdsw = &kbdsw_ini;
82128345Stjr
83128345Stjrstatic int keymap_restrict_change;
84128345StjrSYSCTL_NODE(_hw, OID_AUTO, kbd, CTLFLAG_RD, 0, "kbd");
85128345StjrSYSCTL_INT(_hw_kbd, OID_AUTO, keymap_restrict_change, CTLFLAG_RW,
86128345Stjr    &keymap_restrict_change, 0, "restrict ability to change keymap");
87128345Stjr
88128345Stjr#define ARRAY_DELTA	4
89294286Sdelphij
90128345Stjrstatic int
91128345Stjrkbd_realloc_array(void)
92128345Stjr{
93128345Stjr	keyboard_t **new_kbd;
94128345Stjr	keyboard_switch_t **new_kbdsw;
95128345Stjr	int newsize;
96128345Stjr	int s;
97128345Stjr
9860786Sps	s = spltty();
9960786Sps	newsize = ((keyboards + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA;
10060786Sps	new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT|M_ZERO);
10160786Sps	if (new_kbd == NULL) {
102128345Stjr		splx(s);
103128345Stjr		return (ENOMEM);
104128345Stjr	}
105128345Stjr	new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF,
106128345Stjr			    M_NOWAIT|M_ZERO);
107128345Stjr	if (new_kbdsw == NULL) {
108128345Stjr		free(new_kbd, M_DEVBUF);
109128345Stjr		splx(s);
110128345Stjr		return (ENOMEM);
111128345Stjr	}
112128345Stjr	bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards);
113128345Stjr	bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards);
114128345Stjr	if (keyboards > 1) {
115128345Stjr		free(keyboard, M_DEVBUF);
116128345Stjr		free(kbdsw, M_DEVBUF);
117128345Stjr	}
118128345Stjr	keyboard = new_kbd;
119128345Stjr	kbdsw = new_kbdsw;
120128345Stjr	keyboards = newsize;
121128345Stjr	splx(s);
122128345Stjr
123128345Stjr	if (bootverbose)
124128345Stjr		printf("kbd: new array size %d\n", keyboards);
125128345Stjr
126128345Stjr	return (0);
127128345Stjr}
128128345Stjr
129128345Stjr/*
130128345Stjr * Low-level keyboard driver functions
131128345Stjr * Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard
132128345Stjr * driver, call these functions to initialize the keyboard_t structure
133128345Stjr * and register it to the virtual keyboard driver `kbd'.
134128345Stjr */
135128345Stjr
136128345Stjr/* initialize the keyboard_t structure */
137128345Stjrvoid
138128345Stjrkbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config,
139128345Stjr		int port, int port_size)
140128345Stjr{
141128345Stjr	kbd->kb_flags = KB_NO_DEVICE;	/* device has not been found */
142128345Stjr	kbd->kb_name = name;
143128345Stjr	kbd->kb_type = type;
144128345Stjr	kbd->kb_unit = unit;
145128345Stjr	kbd->kb_config = config & ~KB_CONF_PROBE_ONLY;
146128345Stjr	kbd->kb_led = 0;		/* unknown */
147128345Stjr	kbd->kb_io_base = port;
148128345Stjr	kbd->kb_io_size = port_size;
149128345Stjr	kbd->kb_data = NULL;
150128345Stjr	kbd->kb_keymap = NULL;
151128345Stjr	kbd->kb_accentmap = NULL;
152128345Stjr	kbd->kb_fkeytab = NULL;
153294286Sdelphij	kbd->kb_fkeytab_size = 0;
154128345Stjr	kbd->kb_delay1 = KB_DELAY1;	/* these values are advisory only */
155128345Stjr	kbd->kb_delay2 = KB_DELAY2;
156128345Stjr	kbd->kb_count = 0L;
157128345Stjr	bzero(kbd->kb_lastact, sizeof(kbd->kb_lastact));
158128345Stjr}
159128345Stjr
160128345Stjrvoid
161128345Stjrkbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
162128345Stjr	     fkeytab_t *fkeymap, int fkeymap_size)
163128345Stjr{
164128345Stjr	kbd->kb_keymap = keymap;
165128345Stjr	kbd->kb_accentmap = accmap;
166128345Stjr	kbd->kb_fkeytab = fkeymap;
167128345Stjr	kbd->kb_fkeytab_size = fkeymap_size;
168128345Stjr}
169128345Stjr
170128345Stjr/* declare a new keyboard driver */
171128345Stjrint
172128345Stjrkbd_add_driver(keyboard_driver_t *driver)
173128345Stjr{
174128345Stjr	if (SLIST_NEXT(driver, link))
175128345Stjr		return (EINVAL);
176128345Stjr	SLIST_INSERT_HEAD(&keyboard_drivers, driver, link);
177128345Stjr	return (0);
178128345Stjr}
179128345Stjr
180128345Stjrint
181128345Stjrkbd_delete_driver(keyboard_driver_t *driver)
182128345Stjr{
183128345Stjr	SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link);
184128345Stjr	SLIST_NEXT(driver, link) = NULL;
185128345Stjr	return (0);
186128345Stjr}
187128345Stjr
188128345Stjr/* register a keyboard and associate it with a function table */
189128345Stjrint
190128345Stjrkbd_register(keyboard_t *kbd)
191294286Sdelphij{
192128345Stjr	const keyboard_driver_t **list;
193128345Stjr	const keyboard_driver_t *p;
194128345Stjr	keyboard_t *mux;
195128345Stjr	keyboard_info_t ki;
196128345Stjr	int index;
197128345Stjr
198128345Stjr	mux = kbd_get_keyboard(kbd_find_keyboard("kbdmux", -1));
199161475Sdelphij
200128345Stjr	for (index = 0; index < keyboards; ++index) {
201128345Stjr		if (keyboard[index] == NULL)
202128345Stjr			break;
203128345Stjr	}
204128345Stjr	if (index >= keyboards) {
205128345Stjr		if (kbd_realloc_array())
206128345Stjr			return (-1);
207128345Stjr	}
208128345Stjr
209128345Stjr	kbd->kb_index = index;
210128345Stjr	KBD_UNBUSY(kbd);
211128345Stjr	KBD_VALID(kbd);
212128345Stjr	kbd->kb_active = 0;	/* disabled until someone calls kbd_enable() */
213128345Stjr	kbd->kb_token = NULL;
214128345Stjr	kbd->kb_callback.kc_func = NULL;
215128345Stjr	kbd->kb_callback.kc_arg = NULL;
216128345Stjr
217128345Stjr	SLIST_FOREACH(p, &keyboard_drivers, link) {
218128345Stjr		if (strcmp(p->name, kbd->kb_name) == 0) {
219128345Stjr			keyboard[index] = kbd;
22060786Sps			kbdsw[index] = p->kbdsw;
22160786Sps
22260786Sps			if (mux != NULL) {
22360786Sps				bzero(&ki, sizeof(ki));
22460786Sps				strcpy(ki.kb_name, kbd->kb_name);
22560786Sps				ki.kb_unit = kbd->kb_unit;
22660786Sps
22760786Sps				(void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
22860786Sps			}
22960786Sps
230161475Sdelphij			return (index);
23160786Sps		}
23260786Sps	}
23360786Sps	SET_FOREACH(list, kbddriver_set) {
23460786Sps		p = *list;
23560786Sps		if (strcmp(p->name, kbd->kb_name) == 0) {
23660786Sps			keyboard[index] = kbd;
23760786Sps			kbdsw[index] = p->kbdsw;
238294286Sdelphij
239161475Sdelphij			if (mux != NULL) {
24060786Sps				bzero(&ki, sizeof(ki));
24160786Sps				strcpy(ki.kb_name, kbd->kb_name);
242161475Sdelphij				ki.kb_unit = kbd->kb_unit;
24360786Sps
24460786Sps				(void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
24560786Sps			}
246128345Stjr
24760786Sps			return (index);
24860786Sps		}
24960786Sps	}
25060786Sps
25160786Sps	return (-1);
25260786Sps}
25360786Sps
25460786Spsint
25560786Spskbd_unregister(keyboard_t *kbd)
25660786Sps{
25760786Sps	int error;
25860786Sps	int s;
25960786Sps
26060786Sps	if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards))
26160786Sps		return (ENOENT);
26260786Sps	if (keyboard[kbd->kb_index] != kbd)
26360786Sps		return (ENOENT);
26460786Sps
26560786Sps	s = spltty();
26660786Sps	if (KBD_IS_BUSY(kbd)) {
26760786Sps		error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING,
26860786Sps		    kbd->kb_callback.kc_arg);
26960786Sps		if (error) {
27060786Sps			splx(s);
27160786Sps			return (error);
27260786Sps		}
27360786Sps		if (KBD_IS_BUSY(kbd)) {
27460786Sps			splx(s);
27560786Sps			return (EBUSY);
27660786Sps		}
27760786Sps	}
27860786Sps	KBD_INVALID(kbd);
27960786Sps	keyboard[kbd->kb_index] = NULL;
28060786Sps	kbdsw[kbd->kb_index] = NULL;
28160786Sps
28260786Sps	splx(s);
28360786Sps	return (0);
28460786Sps}
28560786Sps
28660786Sps/* find a funciton table by the driver name */
28760786Spskeyboard_switch_t
28860786Sps*kbd_get_switch(char *driver)
28960786Sps{
29060786Sps	const keyboard_driver_t **list;
29160786Sps	const keyboard_driver_t *p;
29260786Sps
29360786Sps	SLIST_FOREACH(p, &keyboard_drivers, link) {
29460786Sps		if (strcmp(p->name, driver) == 0)
29560786Sps			return (p->kbdsw);
29660786Sps	}
29760786Sps	SET_FOREACH(list, kbddriver_set) {
29860786Sps		p = *list;
29960786Sps		if (strcmp(p->name, driver) == 0)
30060786Sps			return (p->kbdsw);
30160786Sps	}
30260786Sps
30360786Sps	return (NULL);
30460786Sps}
30560786Sps
30660786Sps/*
30760786Sps * Keyboard client functions
30860786Sps * Keyboard clients, such as the console driver `syscons' and the keyboard
30960786Sps * cdev driver, use these functions to claim and release a keyboard for
31060786Sps * exclusive use.
31160786Sps */
31260786Sps
31360786Sps/*
31460786Sps * find the keyboard specified by a driver name and a unit number
31560786Sps * starting at given index
31660786Sps */
31760786Spsint
31860786Spskbd_find_keyboard2(char *driver, int unit, int index)
31960786Sps{
32060786Sps	int i;
32160786Sps
32260786Sps	if ((index < 0) || (index >= keyboards))
32360786Sps		return (-1);
32460786Sps
32560786Sps	for (i = index; i < keyboards; ++i) {
32660786Sps		if (keyboard[i] == NULL)
32760786Sps			continue;
32860786Sps		if (!KBD_IS_VALID(keyboard[i]))
32960786Sps			continue;
33060786Sps		if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver))
33160786Sps			continue;
33260786Sps		if ((unit != -1) && (keyboard[i]->kb_unit != unit))
33360786Sps			continue;
33460786Sps		return (i);
33560786Sps	}
33660786Sps
33760786Sps	return (-1);
33860786Sps}
33960786Sps
34060786Sps/* find the keyboard specified by a driver name and a unit number */
34160786Spsint
34260786Spskbd_find_keyboard(char *driver, int unit)
34360786Sps{
34460786Sps	return (kbd_find_keyboard2(driver, unit, 0));
34560786Sps}
34660786Sps
34760786Sps/* allocate a keyboard */
34860786Spsint
34960786Spskbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func,
35060786Sps	     void *arg)
35160786Sps{
35260786Sps	int index;
353294286Sdelphij	int s;
35460786Sps
35560786Sps	if (func == NULL)
35660786Sps		return (-1);
35760786Sps
35860786Sps	s = spltty();
35960786Sps	index = kbd_find_keyboard(driver, unit);
36060786Sps	if (index >= 0) {
36160786Sps		if (KBD_IS_BUSY(keyboard[index])) {
36260786Sps			splx(s);
36360786Sps			return (-1);
36460786Sps		}
36560786Sps		keyboard[index]->kb_token = id;
36660786Sps		KBD_BUSY(keyboard[index]);
36760786Sps		keyboard[index]->kb_callback.kc_func = func;
36860786Sps		keyboard[index]->kb_callback.kc_arg = arg;
36960786Sps		kbdd_clear_state(keyboard[index]);
37060786Sps	}
37160786Sps	splx(s);
37260786Sps	return (index);
37360786Sps}
37460786Sps
37560786Spsint
37660786Spskbd_release(keyboard_t *kbd, void *id)
37760786Sps{
37860786Sps	int error;
37960786Sps	int s;
38060786Sps
38160786Sps	s = spltty();
38260786Sps	if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
38360786Sps		error = EINVAL;
38460786Sps	} else if (kbd->kb_token != id) {
38560786Sps		error = EPERM;
38660786Sps	} else {
38760786Sps		kbd->kb_token = NULL;
38860786Sps		KBD_UNBUSY(kbd);
38960786Sps		kbd->kb_callback.kc_func = NULL;
39060786Sps		kbd->kb_callback.kc_arg = NULL;
39160786Sps		kbdd_clear_state(kbd);
39260786Sps		error = 0;
39360786Sps	}
39460786Sps	splx(s);
39560786Sps	return (error);
39660786Sps}
39760786Sps
39860786Spsint
39960786Spskbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func,
40060786Sps		    void *arg)
40160786Sps{
402221715Sdelphij	int error;
40360786Sps	int s;
40460786Sps
40560786Sps	s = spltty();
40660786Sps	if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
40760786Sps		error = EINVAL;
40860786Sps	} else if (kbd->kb_token != id) {
40960786Sps		error = EPERM;
41060786Sps	} else if (func == NULL) {
41160786Sps		error = EINVAL;
41260786Sps	} else {
41360786Sps		kbd->kb_callback.kc_func = func;
414128345Stjr		kbd->kb_callback.kc_arg = arg;
41560786Sps		error = 0;
41660786Sps	}
41760786Sps	splx(s);
41860786Sps	return (error);
41960786Sps}
42060786Sps
42160786Sps/* get a keyboard structure */
42260786Spskeyboard_t
42360786Sps*kbd_get_keyboard(int index)
42460786Sps{
42560786Sps	if ((index < 0) || (index >= keyboards))
42660786Sps		return (NULL);
42760786Sps	if (keyboard[index] == NULL)
42860786Sps		return (NULL);
42960786Sps	if (!KBD_IS_VALID(keyboard[index]))
43060786Sps		return (NULL);
431161475Sdelphij	return (keyboard[index]);
43260786Sps}
43360786Sps
43460786Sps/*
435294286Sdelphij * The back door for the console driver; configure keyboards
436161475Sdelphij * This function is for the kernel console to initialize keyboards
43760786Sps * at very early stage.
438161475Sdelphij */
43960786Sps
440161475Sdelphijint
44160786Spskbd_configure(int flags)
44260786Sps{
443161475Sdelphij	const keyboard_driver_t **list;
444294286Sdelphij	const keyboard_driver_t *p;
445161475Sdelphij
446161475Sdelphij	SLIST_FOREACH(p, &keyboard_drivers, link) {
447161475Sdelphij		if (p->configure != NULL)
44860786Sps			(*p->configure)(flags);
449128345Stjr	}
450128345Stjr	SET_FOREACH(list, kbddriver_set) {
45160786Sps		p = *list;
45260786Sps		if (p->configure != NULL)
45360786Sps			(*p->configure)(flags);
45460786Sps	}
45560786Sps
456128345Stjr	return (0);
457128345Stjr}
45860786Sps
459128345Stjr#ifdef KBD_INSTALL_CDEV
46060786Sps
461128345Stjr/*
46260786Sps * Virtual keyboard cdev driver functions
46360786Sps * The virtual keyboard driver dispatches driver functions to
46460786Sps * appropriate subdrivers.
46560786Sps */
46660786Sps
46760786Sps#define KBD_UNIT(dev)	dev2unit(dev)
46860786Sps
46960786Spsstatic d_open_t		genkbdopen;
47060786Spsstatic d_close_t	genkbdclose;
47160786Spsstatic d_read_t		genkbdread;
47260786Spsstatic d_write_t	genkbdwrite;
47360786Spsstatic d_ioctl_t	genkbdioctl;
474170256Sdelphijstatic d_poll_t		genkbdpoll;
475191930Sdelphij
476191930Sdelphij
477191930Sdelphijstatic struct cdevsw kbd_cdevsw = {
47860786Sps	.d_version =	D_VERSION,
47960786Sps	.d_flags =	D_NEEDGIANT,
48060786Sps	.d_open =	genkbdopen,
481173682Sdelphij	.d_close =	genkbdclose,
48260786Sps	.d_read =	genkbdread,
48360786Sps	.d_write =	genkbdwrite,
484294286Sdelphij	.d_ioctl =	genkbdioctl,
485294286Sdelphij	.d_poll =	genkbdpoll,
486294286Sdelphij	.d_name =	"kbd",
487170256Sdelphij};
488294286Sdelphij
489294286Sdelphijint
490294286Sdelphijkbd_attach(keyboard_t *kbd)
491294286Sdelphij{
492294286Sdelphij
493170256Sdelphij	if (kbd->kb_index >= keyboards)
494294286Sdelphij		return (EINVAL);
495294286Sdelphij	if (keyboard[kbd->kb_index] != kbd)
496294286Sdelphij		return (EINVAL);
497294286Sdelphij
498294286Sdelphij	kbd->kb_dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL,
499294286Sdelphij	    0600, "%s%r", kbd->kb_name, kbd->kb_unit);
500294286Sdelphij	make_dev_alias(kbd->kb_dev, "kbd%r", kbd->kb_index);
501294286Sdelphij	kbd->kb_dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF,
502294286Sdelphij	    M_WAITOK | M_ZERO);
503170256Sdelphij	printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit);
504170256Sdelphij	return (0);
505170256Sdelphij}
506170256Sdelphij
507170256Sdelphijint
508170256Sdelphijkbd_detach(keyboard_t *kbd)
50960786Sps{
51060786Sps
51160786Sps	if (kbd->kb_index >= keyboards)
51260786Sps		return (EINVAL);
51360786Sps	if (keyboard[kbd->kb_index] != kbd)
51460786Sps		return (EINVAL);
51560786Sps
51660786Sps	free(kbd->kb_dev->si_drv1, M_DEVBUF);
51760786Sps	destroy_dev(kbd->kb_dev);
51860786Sps
51960786Sps	return (0);
520173682Sdelphij}
52160786Sps
52260786Sps/*
52360786Sps * Generic keyboard cdev driver functions
52460786Sps * Keyboard subdrivers may call these functions to implement common
52560786Sps * driver functions.
52660786Sps */
52760786Sps
52860786Spsstatic void
52960786Spsgenkbd_putc(genkbd_softc_t *sc, char c)
53060786Sps{
53160786Sps	unsigned int p;
53260786Sps
53360786Sps	if (sc->gkb_q_length == KB_QSIZE)
53460786Sps		return;
53560786Sps
53660786Sps	p = (sc->gkb_q_start + sc->gkb_q_length) % KB_QSIZE;
53760786Sps	sc->gkb_q[p] = c;
53860786Sps	sc->gkb_q_length++;
53960786Sps}
54060786Sps
54160786Spsstatic size_t
54260786Spsgenkbd_getc(genkbd_softc_t *sc, char *buf, size_t len)
54360786Sps{
54460786Sps
54560786Sps	/* Determine copy size. */
54660786Sps	if (sc->gkb_q_length == 0)
54760786Sps		return (0);
54860786Sps	if (len >= sc->gkb_q_length)
54960786Sps		len = sc->gkb_q_length;
55060786Sps	if (len >= KB_QSIZE - sc->gkb_q_start)
55160786Sps		len = KB_QSIZE - sc->gkb_q_start;
55260786Sps
55360786Sps	/* Copy out data and progress offset. */
55460786Sps	memcpy(buf, sc->gkb_q + sc->gkb_q_start, len);
55560786Sps	sc->gkb_q_start = (sc->gkb_q_start + len) % KB_QSIZE;
55660786Sps	sc->gkb_q_length -= len;
55760786Sps
55860786Sps	return (len);
55960786Sps}
56060786Sps
56160786Spsstatic kbd_callback_func_t genkbd_event;
56260786Sps
56360786Spsstatic int
56460786Spsgenkbdopen(struct cdev *dev, int mode, int flag, struct thread *td)
56560786Sps{
56660786Sps	keyboard_t *kbd;
56760786Sps	genkbd_softc_t *sc;
56860786Sps	int s;
56960786Sps	int i;
57060786Sps
57160786Sps	s = spltty();
57260786Sps	sc = dev->si_drv1;
57360786Sps	kbd = kbd_get_keyboard(KBD_INDEX(dev));
57460786Sps	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
57560786Sps		splx(s);
57660786Sps		return (ENXIO);
57760786Sps	}
57860786Sps	i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc,
57960786Sps	    genkbd_event, (void *)sc);
58060786Sps	if (i < 0) {
58160786Sps		splx(s);
58260786Sps		return (EBUSY);
58360786Sps	}
58460786Sps	/* assert(i == kbd->kb_index) */
58560786Sps	/* assert(kbd == kbd_get_keyboard(i)) */
58660786Sps
58760786Sps	/*
58860786Sps	 * NOTE: even when we have successfully claimed a keyboard,
58960786Sps	 * the device may still be missing (!KBD_HAS_DEVICE(kbd)).
59060786Sps	 */
59160786Sps
59260786Sps	sc->gkb_q_length = 0;
59360786Sps	splx(s);
59460786Sps
595128345Stjr	return (0);
596128345Stjr}
59760786Sps
598128345Stjrstatic int
599128345Stjrgenkbdclose(struct cdev *dev, int mode, int flag, struct thread *td)
60060786Sps{
601128345Stjr	keyboard_t *kbd;
60260786Sps	genkbd_softc_t *sc;
60360786Sps	int s;
604294286Sdelphij
605161475Sdelphij	/*
606161475Sdelphij	 * NOTE: the device may have already become invalid.
60760786Sps	 * kbd == NULL || !KBD_IS_VALID(kbd)
608128345Stjr	 */
609128345Stjr	s = spltty();
61060786Sps	sc = dev->si_drv1;
61160786Sps	kbd = kbd_get_keyboard(KBD_INDEX(dev));
61260786Sps	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
61360786Sps		/* XXX: we shall be forgiving and don't report error... */
61460786Sps	} else {
61560786Sps		kbd_release(kbd, (void *)sc);
616128345Stjr	}
617128345Stjr	splx(s);
618128345Stjr	return (0);
619128345Stjr}
620128345Stjr
62160786Spsstatic int
62260786Spsgenkbdread(struct cdev *dev, struct uio *uio, int flag)
62360786Sps{
62460786Sps	keyboard_t *kbd;
62560786Sps	genkbd_softc_t *sc;
62660786Sps	u_char buffer[KB_BUFSIZE];
62760786Sps	int len;
62860786Sps	int error;
62960786Sps	int s;
63060786Sps
63160786Sps	/* wait for input */
63260786Sps	s = spltty();
63360786Sps	sc = dev->si_drv1;
63460786Sps	kbd = kbd_get_keyboard(KBD_INDEX(dev));
63560786Sps	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
63660786Sps		splx(s);
63760786Sps		return (ENXIO);
63860786Sps	}
63960786Sps	while (sc->gkb_q_length == 0) {
640128345Stjr		if (flag & O_NONBLOCK) {
64160786Sps			splx(s);
64260786Sps			return (EWOULDBLOCK);
64360786Sps		}
64460786Sps		sc->gkb_flags |= KB_ASLEEP;
64560786Sps		error = tsleep(sc, PZERO | PCATCH, "kbdrea", 0);
64660786Sps		kbd = kbd_get_keyboard(KBD_INDEX(dev));
64760786Sps		if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
64860786Sps			splx(s);
649128345Stjr			return (ENXIO);	/* our keyboard has gone... */
65060786Sps		}
65160786Sps		if (error) {
65260786Sps			sc->gkb_flags &= ~KB_ASLEEP;
65360786Sps			splx(s);
65460786Sps			return (error);
65560786Sps		}
65660786Sps	}
65760786Sps	splx(s);
65860786Sps
65960786Sps	/* copy as much input as possible */
66060786Sps	error = 0;
66160786Sps	while (uio->uio_resid > 0) {
662128345Stjr		len = imin(uio->uio_resid, sizeof(buffer));
663128345Stjr		len = genkbd_getc(sc, buffer, len);
664128345Stjr		if (len <= 0)
665128345Stjr			break;
666128345Stjr		error = uiomove(buffer, len, uio);
667128345Stjr		if (error)
66860786Sps			break;
66960786Sps	}
67060786Sps
67160786Sps	return (error);
67260786Sps}
673128345Stjr
674128345Stjrstatic int
675128345Stjrgenkbdwrite(struct cdev *dev, struct uio *uio, int flag)
676128345Stjr{
677128345Stjr	keyboard_t *kbd;
678128345Stjr
67960786Sps	kbd = kbd_get_keyboard(KBD_INDEX(dev));
68060786Sps	if ((kbd == NULL) || !KBD_IS_VALID(kbd))
68160786Sps		return (ENXIO);
68260786Sps	return (ENODEV);
68360786Sps}
68460786Sps
68560786Spsstatic int
68660786Spsgenkbdioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
68760786Sps{
68860786Sps	keyboard_t *kbd;
68960786Sps	int error;
69060786Sps
69160786Sps	kbd = kbd_get_keyboard(KBD_INDEX(dev));
69260786Sps	if ((kbd == NULL) || !KBD_IS_VALID(kbd))
69360786Sps		return (ENXIO);
69460786Sps	error = kbdd_ioctl(kbd, cmd, arg);
69560786Sps	if (error == ENOIOCTL)
696128345Stjr		error = ENODEV;
697128345Stjr	return (error);
69860786Sps}
69960786Sps
70060786Spsstatic int
70160786Spsgenkbdpoll(struct cdev *dev, int events, struct thread *td)
70260786Sps{
70360786Sps	keyboard_t *kbd;
70460786Sps	genkbd_softc_t *sc;
70560786Sps	int revents;
70660786Sps	int s;
70760786Sps
70860786Sps	revents = 0;
70960786Sps	s = spltty();
71060786Sps	sc = dev->si_drv1;
71160786Sps	kbd = kbd_get_keyboard(KBD_INDEX(dev));
712294286Sdelphij	if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
713128345Stjr		revents =  POLLHUP;	/* the keyboard has gone */
714161475Sdelphij	} else if (events & (POLLIN | POLLRDNORM)) {
715128345Stjr		if (sc->gkb_q_length > 0)
716128345Stjr			revents = events & (POLLIN | POLLRDNORM);
717128345Stjr		else
71860786Sps			selrecord(td, &sc->gkb_rsel);
719294286Sdelphij	}
720128345Stjr	splx(s);
721128345Stjr	return (revents);
722128345Stjr}
723128345Stjr
724128345Stjrstatic int
725128345Stjrgenkbd_event(keyboard_t *kbd, int event, void *arg)
726128345Stjr{
727128345Stjr	genkbd_softc_t *sc;
728128345Stjr	size_t len;
729128345Stjr	u_char *cp;
730128345Stjr	int mode;
731128345Stjr	u_int c;
732128345Stjr
733128345Stjr	/* assert(KBD_IS_VALID(kbd)) */
734128345Stjr	sc = (genkbd_softc_t *)arg;
735128345Stjr
736128345Stjr	switch (event) {
737128345Stjr	case KBDIO_KEYINPUT:
73860786Sps		break;
73960786Sps	case KBDIO_UNLOADING:
74060786Sps		/* the keyboard is going... */
74160786Sps		kbd_release(kbd, (void *)sc);
74260786Sps		if (sc->gkb_flags & KB_ASLEEP) {
74360786Sps			sc->gkb_flags &= ~KB_ASLEEP;
74460786Sps			wakeup(sc);
74560786Sps		}
74660786Sps		selwakeuppri(&sc->gkb_rsel, PZERO);
74760786Sps		return (0);
74860786Sps	default:
74960786Sps		return (EINVAL);
75060786Sps	}
75160786Sps
75260786Sps	/* obtain the current key input mode */
75360786Sps	if (kbdd_ioctl(kbd, KDGKBMODE, (caddr_t)&mode))
75460786Sps		mode = K_XLATE;
75560786Sps
75660786Sps	/* read all pending input */
75760786Sps	while (kbdd_check_char(kbd)) {
758128345Stjr		c = kbdd_read_char(kbd, FALSE);
759161475Sdelphij		if (c == NOKEY)
76060786Sps			continue;
761128345Stjr		if (c == ERRKEY)	/* XXX: ring bell? */
762128345Stjr			continue;
763128345Stjr		if (!KBD_IS_BUSY(kbd))
764128345Stjr			/* the device is not open, discard the input */
765128345Stjr			continue;
76660786Sps
76760786Sps		/* store the byte as is for K_RAW and K_CODE modes */
76860786Sps		if (mode != K_XLATE) {
76960786Sps			genkbd_putc(sc, KEYCHAR(c));
770128345Stjr			continue;
771128345Stjr		}
772128345Stjr
77360786Sps		/* K_XLATE */
77460786Sps		if (c & RELKEY)	/* key release is ignored */
77560786Sps			continue;
776294286Sdelphij
777161475Sdelphij		/* process special keys; most of them are just ignored... */
778161475Sdelphij		if (c & SPCLKEY) {
779128345Stjr			switch (KEYCHAR(c)) {
780128345Stjr			default:
781128345Stjr				/* ignore them... */
782128345Stjr				continue;
78360786Sps			case BTAB:	/* a backtab: ESC [ Z */
78460786Sps				genkbd_putc(sc, 0x1b);
78560786Sps				genkbd_putc(sc, '[');
78660786Sps				genkbd_putc(sc, 'Z');
78760786Sps				continue;
78860786Sps			}
78960786Sps		}
79060786Sps
79160786Sps		/* normal chars, normal chars with the META, function keys */
79260786Sps		switch (KEYFLAGS(c)) {
79360786Sps		case 0:			/* a normal char */
79460786Sps			genkbd_putc(sc, KEYCHAR(c));
79560786Sps			break;
79660786Sps		case MKEY:		/* the META flag: prepend ESC */
79760786Sps			genkbd_putc(sc, 0x1b);
79860786Sps			genkbd_putc(sc, KEYCHAR(c));
79960786Sps			break;
80060786Sps		case FKEY | SPCLKEY:	/* a function key, return string */
80160786Sps			cp = kbdd_get_fkeystr(kbd, KEYCHAR(c), &len);
80260786Sps			if (cp != NULL) {
80360786Sps				while (len-- >  0)
80460786Sps					genkbd_putc(sc, *cp++);
80560786Sps			}
80660786Sps			break;
80760786Sps		}
80860786Sps	}
80960786Sps
81060786Sps	/* wake up sleeping/polling processes */
81160786Sps	if (sc->gkb_q_length > 0) {
81260786Sps		if (sc->gkb_flags & KB_ASLEEP) {
81360786Sps			sc->gkb_flags &= ~KB_ASLEEP;
81460786Sps			wakeup(sc);
81560786Sps		}
81660786Sps		selwakeuppri(&sc->gkb_rsel, PZERO);
817237613Sdelphij	}
818237613Sdelphij
819237613Sdelphij	return (0);
820237613Sdelphij}
821237613Sdelphij
822237613Sdelphij#endif /* KBD_INSTALL_CDEV */
823237613Sdelphij
824294286Sdelphij/*
825237613Sdelphij * Generic low-level keyboard functions
826294286Sdelphij * The low-level functions in the keyboard subdriver may use these
827237613Sdelphij * functions.
828294286Sdelphij */
829294286Sdelphij
830294286Sdelphij#ifndef KBD_DISABLE_KEYMAP_LOAD
831294286Sdelphijstatic int key_change_ok(struct keyent_t *, struct keyent_t *, struct thread *);
832294286Sdelphijstatic int keymap_change_ok(keymap_t *, keymap_t *, struct thread *);
833294286Sdelphijstatic int accent_change_ok(accentmap_t *, accentmap_t *, struct thread *);
834294286Sdelphijstatic int fkey_change_ok(fkeytab_t *, fkeyarg_t *, struct thread *);
835294286Sdelphij#endif
836294286Sdelphij
837294286Sdelphijint
838237613Sdelphijgenkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
839237613Sdelphij{
840237613Sdelphij#ifndef KBD_DISABLE_KEYMAP_LOAD
841237613Sdelphij	keymap_t *mapp;
842237613Sdelphij#endif
84360786Sps	keyarg_t *keyp;
84460786Sps	fkeyarg_t *fkeyp;
84560786Sps	int s;
84660786Sps	int i;
84760786Sps	int error;
84860786Sps
84960786Sps	s = spltty();
85060786Sps	switch (cmd) {
85160786Sps
85260786Sps	case KDGKBINFO:		/* get keyboard information */
85360786Sps		((keyboard_info_t *)arg)->kb_index = kbd->kb_index;
85460786Sps		i = imin(strlen(kbd->kb_name) + 1,
85560786Sps		    sizeof(((keyboard_info_t *)arg)->kb_name));
85660786Sps		bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i);
857161475Sdelphij		((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit;
85860786Sps		((keyboard_info_t *)arg)->kb_type = kbd->kb_type;
85960786Sps		((keyboard_info_t *)arg)->kb_config = kbd->kb_config;
86060786Sps		((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags;
86160786Sps		break;
86260786Sps
863128345Stjr	case KDGKBTYPE:		/* get keyboard type */
86460786Sps		*(int *)arg = kbd->kb_type;
86560786Sps		break;
86660786Sps
86760786Sps	case KDGETREPEAT:	/* get keyboard repeat rate */
868237613Sdelphij		((int *)arg)[0] = kbd->kb_delay1;
86960786Sps		((int *)arg)[1] = kbd->kb_delay2;
87060786Sps		break;
87160786Sps
87260786Sps	case GIO_KEYMAP:	/* get keyboard translation table */
87360786Sps		error = copyout(kbd->kb_keymap, *(void **)arg,
874191930Sdelphij		    sizeof(keymap_t));
875191930Sdelphij		splx(s);
876191930Sdelphij		return (error);
877191930Sdelphij	case PIO_KEYMAP:	/* set keyboard translation table */
87860786Sps#ifndef KBD_DISABLE_KEYMAP_LOAD
879237613Sdelphij		mapp = malloc(sizeof *mapp, M_TEMP, M_NOWAIT);
88060786Sps		error = copyin(*(void **)arg, mapp, sizeof *mapp);
88160786Sps		if (error != 0) {
882195941Sdelphij			splx(s);
883195941Sdelphij			free(mapp, M_TEMP);
884195941Sdelphij			return (error);
885195941Sdelphij		}
886195941Sdelphij
887195941Sdelphij		error = keymap_change_ok(kbd->kb_keymap, mapp, curthread);
888195941Sdelphij		if (error != 0) {
889195941Sdelphij			splx(s);
890195941Sdelphij			free(mapp, M_TEMP);
891237613Sdelphij			return (error);
892237613Sdelphij		}
893237613Sdelphij		bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
894237613Sdelphij		bcopy(mapp, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
895237613Sdelphij		free(mapp, M_TEMP);
89660786Sps		break;
897294286Sdelphij#else
898161475Sdelphij		splx(s);
899161475Sdelphij		return (ENODEV);
90060786Sps#endif
90160786Sps
90260786Sps	case GIO_KEYMAPENT:	/* get keyboard translation table entry */
90360786Sps		keyp = (keyarg_t *)arg;
90460786Sps		if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /
90560786Sps		    sizeof(kbd->kb_keymap->key[0])) {
90660786Sps			splx(s);
90760786Sps			return (EINVAL);
90860786Sps		}
90960786Sps		bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key,
91060786Sps		    sizeof(keyp->key));
91160786Sps		break;
91260786Sps	case PIO_KEYMAPENT:	/* set keyboard translation table entry */
91360786Sps#ifndef KBD_DISABLE_KEYMAP_LOAD
91460786Sps		keyp = (keyarg_t *)arg;
91560786Sps		if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /
91660786Sps		    sizeof(kbd->kb_keymap->key[0])) {
91760786Sps			splx(s);
91860786Sps			return (EINVAL);
91960786Sps		}
92060786Sps		error = key_change_ok(&kbd->kb_keymap->key[keyp->keynum],
92160786Sps		    &keyp->key, curthread);
92260786Sps		if (error != 0) {
92360786Sps			splx(s);
924237613Sdelphij			return (error);
925237613Sdelphij		}
926237613Sdelphij		bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
927237613Sdelphij		    sizeof(keyp->key));
928237613Sdelphij		break;
92960786Sps#else
930237613Sdelphij		splx(s);
931237613Sdelphij		return (ENODEV);
932237613Sdelphij#endif
933237613Sdelphij
934237613Sdelphij	case GIO_DEADKEYMAP:	/* get accent key translation table */
935237613Sdelphij		bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap));
93660786Sps		break;
93760786Sps	case PIO_DEADKEYMAP:	/* set accent key translation table */
93860786Sps#ifndef KBD_DISABLE_KEYMAP_LOAD
93960786Sps		error = accent_change_ok(kbd->kb_accentmap,
94060786Sps		    (accentmap_t *)arg, curthread);
94160786Sps		if (error != 0) {
94260786Sps			splx(s);
94360786Sps			return (error);
944128345Stjr		}
94560786Sps		bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
946128345Stjr		break;
94760786Sps#else
94860786Sps		splx(s);
94960786Sps		return (ENODEV);
95060786Sps#endif
951128345Stjr
95260786Sps	case GETFKEY:		/* get functionkey string */
95360786Sps		fkeyp = (fkeyarg_t *)arg;
95460786Sps		if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
95560786Sps			splx(s);
95660786Sps			return (EINVAL);
95760786Sps		}
95860786Sps		bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef,
95960786Sps		    kbd->kb_fkeytab[fkeyp->keynum].len);
96060786Sps		fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len;
96160786Sps		break;
96260786Sps	case SETFKEY:		/* set functionkey string */
96360786Sps#ifndef KBD_DISABLE_KEYMAP_LOAD
96460786Sps		fkeyp = (fkeyarg_t *)arg;
96560786Sps		if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
96660786Sps			splx(s);
96760786Sps			return (EINVAL);
968161475Sdelphij		}
96960786Sps		error = fkey_change_ok(&kbd->kb_fkeytab[fkeyp->keynum],
97060786Sps		    fkeyp, curthread);
97160786Sps		if (error != 0) {
97260786Sps			splx(s);
97389019Sps			return (error);
97489019Sps		}
97589019Sps		kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK);
97689019Sps		bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
97789019Sps		    kbd->kb_fkeytab[fkeyp->keynum].len);
97889019Sps		break;
97989019Sps#else
98089019Sps		splx(s);
98160786Sps		return (ENODEV);
98289019Sps#endif
98360786Sps
98460786Sps	default:
985237613Sdelphij		splx(s);
986237613Sdelphij		return (ENOIOCTL);
987294286Sdelphij	}
988237613Sdelphij
989237613Sdelphij	splx(s);
990294286Sdelphij	return (0);
991161475Sdelphij}
992161475Sdelphij
99360786Sps#ifndef KBD_DISABLE_KEYMAP_LOAD
99460786Sps#define RESTRICTED_KEY(key, i) \
99560786Sps	((key->spcl & (0x80 >> i)) && \
99660786Sps		(key->map[i] == RBT || key->map[i] == SUSP || \
99760786Sps		 key->map[i] == STBY || key->map[i] == DBG || \
99860786Sps		 key->map[i] == PNC || key->map[i] == HALT || \
99960786Sps		 key->map[i] == PDWN))
100060786Sps
100160786Spsstatic int
100260786Spskey_change_ok(struct keyent_t *oldkey, struct keyent_t *newkey, struct thread *td)
100360786Sps{
100460786Sps	int i;
100560786Sps
100660786Sps	/* Low keymap_restrict_change means any changes are OK. */
100760786Sps	if (keymap_restrict_change <= 0)
100860786Sps		return (0);
1009128345Stjr
101060786Sps	/* High keymap_restrict_change means only root can change the keymap. */
101160786Sps	if (keymap_restrict_change >= 2) {
101260786Sps		for (i = 0; i < NUM_STATES; i++)
101360786Sps			if (oldkey->map[i] != newkey->map[i])
101460786Sps				return priv_check(td, PRIV_KEYBOARD);
101560786Sps		if (oldkey->spcl != newkey->spcl)
101660786Sps			return priv_check(td, PRIV_KEYBOARD);
101760786Sps		if (oldkey->flgs != newkey->flgs)
101860786Sps			return priv_check(td, PRIV_KEYBOARD);
101960786Sps		return (0);
102060786Sps	}
102160786Sps
102260786Sps	/* Otherwise we have to see if any special keys are being changed. */
102360786Sps	for (i = 0; i < NUM_STATES; i++) {
102460786Sps		/*
102560786Sps		 * If either the oldkey or the newkey action is restricted
102660786Sps		 * then we must make sure that the action doesn't change.
102760786Sps		 */
102860786Sps		if (!RESTRICTED_KEY(oldkey, i) && !RESTRICTED_KEY(newkey, i))
102960786Sps			continue;
103060786Sps		if ((oldkey->spcl & (0x80 >> i)) == (newkey->spcl & (0x80 >> i))
103160786Sps		    && oldkey->map[i] == newkey->map[i])
103260786Sps			continue;
103360786Sps		return priv_check(td, PRIV_KEYBOARD);
103460786Sps	}
103560786Sps
103660786Sps	return (0);
103760786Sps}
103860786Sps
103960786Spsstatic int
104060786Spskeymap_change_ok(keymap_t *oldmap, keymap_t *newmap, struct thread *td)
104160786Sps{
104260786Sps	int keycode, error;
104360786Sps
104460786Sps	for (keycode = 0; keycode < NUM_KEYS; keycode++) {
1045128345Stjr		if ((error = key_change_ok(&oldmap->key[keycode],
1046170256Sdelphij		    &newmap->key[keycode], td)) != 0)
104760786Sps			return (error);
1048128345Stjr	}
104960786Sps	return (0);
1050128345Stjr}
105160786Sps
105260786Spsstatic int
1053128345Stjraccent_change_ok(accentmap_t *oldmap, accentmap_t *newmap, struct thread *td)
105460786Sps{
105560786Sps	struct acc_t *oldacc, *newacc;
105660786Sps	int accent, i;
105760786Sps
105860786Sps	if (keymap_restrict_change <= 2)
105960786Sps		return (0);
106060786Sps
106160786Sps	if (oldmap->n_accs != newmap->n_accs)
106260786Sps		return priv_check(td, PRIV_KEYBOARD);
106360786Sps
106460786Sps	for (accent = 0; accent < oldmap->n_accs; accent++) {
106560786Sps		oldacc = &oldmap->acc[accent];
106660786Sps		newacc = &newmap->acc[accent];
106760786Sps		if (oldacc->accchar != newacc->accchar)
106860786Sps			return priv_check(td, PRIV_KEYBOARD);
106960786Sps		for (i = 0; i < NUM_ACCENTCHARS; ++i) {
107060786Sps			if (oldacc->map[i][0] != newacc->map[i][0])
107160786Sps				return priv_check(td, PRIV_KEYBOARD);
107260786Sps			if (oldacc->map[i][0] == 0)	/* end of table */
107360786Sps				break;
107460786Sps			if (oldacc->map[i][1] != newacc->map[i][1])
107560786Sps				return priv_check(td, PRIV_KEYBOARD);
107660786Sps		}
107760786Sps	}
107860786Sps
107960786Sps	return (0);
108060786Sps}
108160786Sps
108260786Spsstatic int
108360786Spsfkey_change_ok(fkeytab_t *oldkey, fkeyarg_t *newkey, struct thread *td)
108460786Sps{
108560786Sps	if (keymap_restrict_change <= 3)
108660786Sps		return (0);
108760786Sps
108860786Sps	if (oldkey->len != newkey->flen ||
108960786Sps	    bcmp(oldkey->str, newkey->keydef, oldkey->len) != 0)
109060786Sps		return priv_check(td, PRIV_KEYBOARD);
109160786Sps
109260786Sps	return (0);
109360786Sps}
109460786Sps#endif
109560786Sps
109660786Sps/* get a pointer to the string associated with the given function key */
109760786Spsu_char
109860786Sps*genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
109960786Sps{
110060786Sps	if (kbd == NULL)
110160786Sps		return (NULL);
110260786Sps	fkey -= F_FN;
110360786Sps	if (fkey > kbd->kb_fkeytab_size)
110460786Sps		return (NULL);
1105128345Stjr	*len = kbd->kb_fkeytab[fkey].len;
1106128345Stjr	return (kbd->kb_fkeytab[fkey].str);
1107128345Stjr}
1108128345Stjr
1109128345Stjr/* diagnostic dump */
1110128345Stjrstatic char
1111128345Stjr*get_kbd_type_name(int type)
1112128345Stjr{
1113221715Sdelphij	static struct {
1114221715Sdelphij		int type;
1115221715Sdelphij		char *name;
1116221715Sdelphij	} name_table[] = {
1117221715Sdelphij		{ KB_84,	"AT 84" },
1118221715Sdelphij		{ KB_101,	"AT 101/102" },
1119221715Sdelphij		{ KB_OTHER,	"generic" },
1120221715Sdelphij	};
1121221715Sdelphij	int i;
1122221715Sdelphij
1123221715Sdelphij	for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) {
1124221715Sdelphij		if (type == name_table[i].type)
1125221715Sdelphij			return (name_table[i].name);
1126221715Sdelphij	}
1127221715Sdelphij	return ("unknown");
1128221715Sdelphij}
1129221715Sdelphij
1130221715Sdelphijvoid
1131221715Sdelphijgenkbd_diag(keyboard_t *kbd, int level)
1132{
1133	if (level > 0) {
1134		printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x",
1135		    kbd->kb_index, kbd->kb_name, kbd->kb_unit,
1136		    get_kbd_type_name(kbd->kb_type), kbd->kb_type,
1137		    kbd->kb_config, kbd->kb_flags);
1138		if (kbd->kb_io_base > 0)
1139			printf(", port:0x%x-0x%x", kbd->kb_io_base,
1140			    kbd->kb_io_base + kbd->kb_io_size - 1);
1141		printf("\n");
1142	}
1143}
1144
1145#define set_lockkey_state(k, s, l)				\
1146	if (!((s) & l ## DOWN)) {				\
1147		int i;						\
1148		(s) |= l ## DOWN;				\
1149		(s) ^= l ## ED;					\
1150		i = (s) & LOCK_MASK;				\
1151		(void)kbdd_ioctl((k), KDSETLED, (caddr_t)&i);	\
1152	}
1153
1154static u_int
1155save_accent_key(keyboard_t *kbd, u_int key, int *accents)
1156{
1157	int i;
1158
1159	/* make an index into the accent map */
1160	i = key - F_ACC + 1;
1161	if ((i > kbd->kb_accentmap->n_accs)
1162	    || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) {
1163		/* the index is out of range or pointing to an empty entry */
1164		*accents = 0;
1165		return (ERRKEY);
1166	}
1167
1168	/*
1169	 * If the same accent key has been hit twice, produce the accent
1170	 * char itself.
1171	 */
1172	if (i == *accents) {
1173		key = kbd->kb_accentmap->acc[i - 1].accchar;
1174		*accents = 0;
1175		return (key);
1176	}
1177
1178	/* remember the index and wait for the next key  */
1179	*accents = i;
1180	return (NOKEY);
1181}
1182
1183static u_int
1184make_accent_char(keyboard_t *kbd, u_int ch, int *accents)
1185{
1186	struct acc_t *acc;
1187	int i;
1188
1189	acc = &kbd->kb_accentmap->acc[*accents - 1];
1190	*accents = 0;
1191
1192	/*
1193	 * If the accent key is followed by the space key,
1194	 * produce the accent char itself.
1195	 */
1196	if (ch == ' ')
1197		return (acc->accchar);
1198
1199	/* scan the accent map */
1200	for (i = 0; i < NUM_ACCENTCHARS; ++i) {
1201		if (acc->map[i][0] == 0)	/* end of table */
1202			break;
1203		if (acc->map[i][0] == ch)
1204			return (acc->map[i][1]);
1205	}
1206	/* this char cannot be accented... */
1207	return (ERRKEY);
1208}
1209
1210int
1211genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate,
1212		 int *accents)
1213{
1214	struct keyent_t *key;
1215	int state = *shiftstate;
1216	int action;
1217	int f;
1218	int i;
1219
1220	i = keycode;
1221	f = state & (AGRS | ALKED);
1222	if ((f == AGRS1) || (f == AGRS2) || (f == ALKED))
1223		i += ALTGR_OFFSET;
1224	key = &kbd->kb_keymap->key[i];
1225	i = ((state & SHIFTS) ? 1 : 0)
1226	    | ((state & CTLS) ? 2 : 0)
1227	    | ((state & ALTS) ? 4 : 0);
1228	if (((key->flgs & FLAG_LOCK_C) && (state & CLKED))
1229		|| ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) )
1230		i ^= 1;
1231
1232	if (up) {	/* break: key released */
1233		action = kbd->kb_lastact[keycode];
1234		kbd->kb_lastact[keycode] = NOP;
1235		switch (action) {
1236		case LSHA:
1237			if (state & SHIFTAON) {
1238				set_lockkey_state(kbd, state, ALK);
1239				state &= ~ALKDOWN;
1240			}
1241			action = LSH;
1242			/* FALL THROUGH */
1243		case LSH:
1244			state &= ~SHIFTS1;
1245			break;
1246		case RSHA:
1247			if (state & SHIFTAON) {
1248				set_lockkey_state(kbd, state, ALK);
1249				state &= ~ALKDOWN;
1250			}
1251			action = RSH;
1252			/* FALL THROUGH */
1253		case RSH:
1254			state &= ~SHIFTS2;
1255			break;
1256		case LCTRA:
1257			if (state & SHIFTAON) {
1258				set_lockkey_state(kbd, state, ALK);
1259				state &= ~ALKDOWN;
1260			}
1261			action = LCTR;
1262			/* FALL THROUGH */
1263		case LCTR:
1264			state &= ~CTLS1;
1265			break;
1266		case RCTRA:
1267			if (state & SHIFTAON) {
1268				set_lockkey_state(kbd, state, ALK);
1269				state &= ~ALKDOWN;
1270			}
1271			action = RCTR;
1272			/* FALL THROUGH */
1273		case RCTR:
1274			state &= ~CTLS2;
1275			break;
1276		case LALTA:
1277			if (state & SHIFTAON) {
1278				set_lockkey_state(kbd, state, ALK);
1279				state &= ~ALKDOWN;
1280			}
1281			action = LALT;
1282			/* FALL THROUGH */
1283		case LALT:
1284			state &= ~ALTS1;
1285			break;
1286		case RALTA:
1287			if (state & SHIFTAON) {
1288				set_lockkey_state(kbd, state, ALK);
1289				state &= ~ALKDOWN;
1290			}
1291			action = RALT;
1292			/* FALL THROUGH */
1293		case RALT:
1294			state &= ~ALTS2;
1295			break;
1296		case ASH:
1297			state &= ~AGRS1;
1298			break;
1299		case META:
1300			state &= ~METAS1;
1301			break;
1302		case NLK:
1303			state &= ~NLKDOWN;
1304			break;
1305		case CLK:
1306#ifndef PC98
1307			state &= ~CLKDOWN;
1308#else
1309			state &= ~CLKED;
1310			i = state & LOCK_MASK;
1311			(void)kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i);
1312#endif
1313			break;
1314		case SLK:
1315			state &= ~SLKDOWN;
1316			break;
1317		case ALK:
1318			state &= ~ALKDOWN;
1319			break;
1320		case NOP:
1321			/* release events of regular keys are not reported */
1322			*shiftstate &= ~SHIFTAON;
1323			return (NOKEY);
1324		}
1325		*shiftstate = state & ~SHIFTAON;
1326		return (SPCLKEY | RELKEY | action);
1327	} else {	/* make: key pressed */
1328		action = key->map[i];
1329		state &= ~SHIFTAON;
1330		if (key->spcl & (0x80 >> i)) {
1331			/* special keys */
1332			if (kbd->kb_lastact[keycode] == NOP)
1333				kbd->kb_lastact[keycode] = action;
1334			if (kbd->kb_lastact[keycode] != action)
1335				action = NOP;
1336			switch (action) {
1337			/* LOCKING KEYS */
1338			case NLK:
1339				set_lockkey_state(kbd, state, NLK);
1340				break;
1341			case CLK:
1342#ifndef PC98
1343				set_lockkey_state(kbd, state, CLK);
1344#else
1345				state |= CLKED;
1346				i = state & LOCK_MASK;
1347				(void)kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i);
1348#endif
1349				break;
1350			case SLK:
1351				set_lockkey_state(kbd, state, SLK);
1352				break;
1353			case ALK:
1354				set_lockkey_state(kbd, state, ALK);
1355				break;
1356			/* NON-LOCKING KEYS */
1357			case SPSC: case RBT:  case SUSP: case STBY:
1358			case DBG:  case NEXT: case PREV: case PNC:
1359			case HALT: case PDWN:
1360				*accents = 0;
1361				break;
1362			case BTAB:
1363				*accents = 0;
1364				action |= BKEY;
1365				break;
1366			case LSHA:
1367				state |= SHIFTAON;
1368				action = LSH;
1369				/* FALL THROUGH */
1370			case LSH:
1371				state |= SHIFTS1;
1372				break;
1373			case RSHA:
1374				state |= SHIFTAON;
1375				action = RSH;
1376				/* FALL THROUGH */
1377			case RSH:
1378				state |= SHIFTS2;
1379				break;
1380			case LCTRA:
1381				state |= SHIFTAON;
1382				action = LCTR;
1383				/* FALL THROUGH */
1384			case LCTR:
1385				state |= CTLS1;
1386				break;
1387			case RCTRA:
1388				state |= SHIFTAON;
1389				action = RCTR;
1390				/* FALL THROUGH */
1391			case RCTR:
1392				state |= CTLS2;
1393				break;
1394			case LALTA:
1395				state |= SHIFTAON;
1396				action = LALT;
1397				/* FALL THROUGH */
1398			case LALT:
1399				state |= ALTS1;
1400				break;
1401			case RALTA:
1402				state |= SHIFTAON;
1403				action = RALT;
1404				/* FALL THROUGH */
1405			case RALT:
1406				state |= ALTS2;
1407				break;
1408			case ASH:
1409				state |= AGRS1;
1410				break;
1411			case META:
1412				state |= METAS1;
1413				break;
1414			case NOP:
1415				*shiftstate = state;
1416				return (NOKEY);
1417			default:
1418				/* is this an accent (dead) key? */
1419				*shiftstate = state;
1420				if (action >= F_ACC && action <= L_ACC) {
1421					action = save_accent_key(kbd, action,
1422								 accents);
1423					switch (action) {
1424					case NOKEY:
1425					case ERRKEY:
1426						return (action);
1427					default:
1428						if (state & METAS)
1429							return (action | MKEY);
1430						else
1431							return (action);
1432					}
1433					/* NOT REACHED */
1434				}
1435				/* other special keys */
1436				if (*accents > 0) {
1437					*accents = 0;
1438					return (ERRKEY);
1439				}
1440				if (action >= F_FN && action <= L_FN)
1441					action |= FKEY;
1442				/* XXX: return fkey string for the FKEY? */
1443				return (SPCLKEY | action);
1444			}
1445			*shiftstate = state;
1446			return (SPCLKEY | action);
1447		} else {
1448			/* regular keys */
1449			kbd->kb_lastact[keycode] = NOP;
1450			*shiftstate = state;
1451			if (*accents > 0) {
1452				/* make an accented char */
1453				action = make_accent_char(kbd, action, accents);
1454				if (action == ERRKEY)
1455					return (action);
1456			}
1457			if (state & METAS)
1458				action |= MKEY;
1459			return (action);
1460		}
1461	}
1462	/* NOT REACHED */
1463}
1464