1139825Simp/*-
242805Skato * Copyright (c) 1999 FreeBSD(98) port team.
342805Skato * All rights reserved.
442805Skato *
542805Skato * Redistribution and use in source and binary forms, with or without
642805Skato * modification, are permitted provided that the following conditions
742805Skato * are met:
842805Skato * 1. Redistributions of source code must retain the above copyright
942805Skato *    notice, this list of conditions and the following disclaimer as
1042805Skato *    the first lines of this file unmodified.
1142805Skato * 2. Redistributions in binary form must reproduce the above copyright
1242805Skato *    notice, this list of conditions and the following disclaimer in the
1342805Skato *    documentation and/or other materials provided with the distribution.
1442805Skato * 3. The name of the author may not be used to endorse or promote products
1542805Skato *    derived from this software without specific prior written permission.
1642805Skato *
1742805Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1842805Skato * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1942805Skato * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2042805Skato * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2142805Skato * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2242805Skato * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2342805Skato * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2442805Skato * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2542805Skato * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2642805Skato * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2742805Skato *
2850477Speter * $FreeBSD$
2942795Skato */
3042795Skato
31162711Sru#include "opt_compat.h"
3242795Skato#include "opt_kbd.h"
3342795Skato
3442795Skato#include <sys/param.h>
3542795Skato#include <sys/systm.h>
3642795Skato#include <sys/kernel.h>
3745783Skato#include <sys/module.h>
3842795Skato#include <sys/bus.h>
3945783Skato#include <machine/bus.h>
4045783Skato#include <sys/rman.h>
4167370Skato#include <sys/kbio.h>
4242795Skato
4342795Skato#include <machine/resource.h>
4442795Skato
4542795Skato#include <dev/kbd/kbdreg.h>
4642795Skato
47146049Snyan#include <pc98/cbus/cbus.h>
4845783Skato#include <isa/isavar.h>
4945783Skato
5042795Skato#define DRIVER_NAME		"pckbd"
5142795Skato
5242795Skato/* device configuration flags */
5342795Skato#define KB_CONF_FAIL_IF_NO_KBD	(1 << 0) /* don't install if no kbd is found */
5442795Skato
5545783Skatostatic devclass_t	pckbd_devclass;
5642795Skato
5745783Skatostatic int		pckbdprobe(device_t dev);
5845783Skatostatic int		pckbdattach(device_t dev);
5979702Snyanstatic int		pckbdresume(device_t dev);
6045783Skatostatic void		pckbd_isa_intr(void *arg);
6142795Skato
6245783Skatostatic device_method_t pckbd_methods[] = {
6345783Skato	/* Device interface */
6445783Skato	DEVMETHOD(device_probe,		pckbdprobe),
6545783Skato	DEVMETHOD(device_attach,	pckbdattach),
6679702Snyan	DEVMETHOD(device_resume,	pckbdresume),
6745783Skato	{ 0, 0 }
6845783Skato};
6942795Skato
7045783Skatostatic driver_t pckbd_driver = {
7142795Skato	DRIVER_NAME,
7245783Skato	pckbd_methods,
7350236Skato	1,
7442795Skato};
7542795Skato
7645783SkatoDRIVER_MODULE(pckbd, isa, pckbd_driver, pckbd_devclass, 0, 0);
7745783Skato
7878385Snyanstatic bus_addr_t pckbd_iat[] = {0, 2};
7978385Snyan
8044635Skatostatic int		pckbd_probe_unit(int unit, int port, int irq,
8144635Skato					 int flags);
8250236Skatostatic int		pckbd_attach_unit(int unit, keyboard_t **kbd,
8344635Skato					  int port, int irq, int flags);
8442795Skatostatic timeout_t	pckbd_timeout;
8542795Skato
8642795Skato
8742795Skatostatic int
8845783Skatopckbdprobe(device_t dev)
8942795Skato{
9078385Snyan	struct resource *res;
9178385Snyan	int error, rid;
9278385Snyan
9351276Snyan	/* Check isapnp ids */
9451276Snyan	if (isa_get_vendorid(dev))
9551276Snyan		return (ENXIO);
9651276Snyan
9745783Skato	device_set_desc(dev, "PC-98 Keyboard");
9845783Skato
9978385Snyan	rid = 0;
10078385Snyan	res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, pckbd_iat, 2,
10178385Snyan				  RF_ACTIVE);
10278385Snyan	if (res == NULL)
10378385Snyan		return ENXIO;
10478385Snyan	isa_load_resourcev(res, pckbd_iat, 2);
10578385Snyan
10678385Snyan	error = pckbd_probe_unit(device_get_unit(dev),
10778385Snyan				 isa_get_port(dev),
10878385Snyan				 (1 << isa_get_irq(dev)),
10978385Snyan				 device_get_flags(dev));
11078385Snyan
11178385Snyan	bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
11278385Snyan
11378385Snyan	return (error);
11442795Skato}
11542795Skato
11642795Skatostatic int
11745783Skatopckbdattach(device_t dev)
11842795Skato{
11950236Skato	keyboard_t	*kbd;
12045783Skato	void		*ih;
12145783Skato	struct resource	*res;
12278385Snyan	int		error, rid;
12342795Skato
12478385Snyan	rid = 0;
12578385Snyan	res = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, pckbd_iat, 2,
12678385Snyan				  RF_ACTIVE);
12778385Snyan	if (res == NULL)
12878385Snyan		return ENXIO;
12978385Snyan	isa_load_resourcev(res, pckbd_iat, 2);
13045783Skato
13178385Snyan	error = pckbd_attach_unit(device_get_unit(dev), &kbd,
13278385Snyan				  isa_get_port(dev),
13378385Snyan				  (1 << isa_get_irq(dev)),
13478385Snyan				  device_get_flags(dev));
13578385Snyan
13678385Snyan	rid = 0;
137127135Snjl	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
13878385Snyan	if (res == NULL)
13978385Snyan		return ENXIO;
140166901Spiso	bus_setup_intr(dev, res, INTR_TYPE_TTY, NULL, pckbd_isa_intr, kbd, &ih);
14145783Skato
14279702Snyan	return 0;
14379702Snyan}
14479702Snyan
14579702Snyanstatic int
14679702Snyanpckbdresume(device_t dev)
14779702Snyan{
14879702Snyan	keyboard_t *kbd;
14979702Snyan
15079702Snyan	kbd = kbd_get_keyboard(kbd_find_keyboard(DRIVER_NAME,
15179702Snyan						 device_get_unit(dev)));
15279702Snyan	if (kbd)
153175001Snyan		kbdd_clear_state(kbd);
15479702Snyan
15545783Skato	return (0);
15642795Skato}
15742795Skato
15842795Skatostatic void
15945783Skatopckbd_isa_intr(void *arg)
16042795Skato{
16150236Skato        keyboard_t	*kbd = arg;
16242795Skato
163175001Snyan	kbdd_intr(kbd, NULL);
16442795Skato}
16542795Skato
16642795Skatostatic int
16744635Skatopckbd_probe_unit(int unit, int port, int irq, int flags)
16842795Skato{
16942795Skato	keyboard_switch_t *sw;
17042795Skato	int args[2];
17144635Skato	int error;
17242795Skato
17342795Skato	sw = kbd_get_switch(DRIVER_NAME);
17442795Skato	if (sw == NULL)
17542795Skato		return ENXIO;
17642795Skato
17742795Skato	args[0] = port;
17842795Skato	args[1] = irq;
17944635Skato	error = (*sw->probe)(unit, args, flags);
18044635Skato	if (error)
18144635Skato		return error;
18244635Skato	return 0;
18342795Skato}
18442795Skato
18542795Skatostatic int
18650236Skatopckbd_attach_unit(int unit, keyboard_t **kbd, int port, int irq, int flags)
18742795Skato{
18842795Skato	keyboard_switch_t *sw;
18944635Skato	int args[2];
19042795Skato	int error;
19142795Skato
19242795Skato	sw = kbd_get_switch(DRIVER_NAME);
19342795Skato	if (sw == NULL)
19442795Skato		return ENXIO;
19542795Skato
19642795Skato	/* reset, initialize and enable the device */
19744635Skato	args[0] = port;
19844635Skato	args[1] = irq;
19950236Skato	*kbd = NULL;
20044635Skato	error = (*sw->probe)(unit, args, flags);
20142795Skato	if (error)
20244635Skato		return error;
20350236Skato	error = (*sw->init)(unit, kbd, args, flags);
20444635Skato	if (error)
20544635Skato		return error;
20650236Skato	(*sw->enable)(*kbd);
20742795Skato
20842795Skato#ifdef KBD_INSTALL_CDEV
20942795Skato	/* attach a virtual keyboard cdev */
21050236Skato	error = kbd_attach(*kbd);
21142795Skato	if (error)
21242795Skato		return error;
21342795Skato#endif /* KBD_INSTALL_CDEV */
21442795Skato
21542795Skato	/*
21642795Skato	 * This is a kludge to compensate for lost keyboard interrupts.
21742795Skato	 * A similar code used to be in syscons. See below. XXX
21842795Skato	 */
21950236Skato	pckbd_timeout(*kbd);
22042795Skato
22142795Skato	if (bootverbose)
22250236Skato		(*sw->diag)(*kbd, bootverbose);
22342795Skato
22442795Skato	return 0;
22542795Skato}
22642795Skato
22742795Skatostatic void
22842795Skatopckbd_timeout(void *arg)
22942795Skato{
23042795Skato	keyboard_t *kbd;
23142795Skato	int s;
23242795Skato
23342795Skato	/* The following comments are extracted from syscons.c (1.287) */
23442795Skato	/*
23542795Skato	 * With release 2.1 of the Xaccel server, the keyboard is left
23642795Skato	 * hanging pretty often. Apparently an interrupt from the
23742795Skato	 * keyboard is lost, and I don't know why (yet).
23842795Skato	 * This ugly hack calls scintr if input is ready for the keyboard
23942795Skato	 * and conveniently hides the problem.			XXX
24042795Skato	 */
24142795Skato	/*
24242795Skato	 * Try removing anything stuck in the keyboard controller; whether
24342795Skato	 * it's a keyboard scan code or mouse data. `scintr()' doesn't
24442795Skato	 * read the mouse data directly, but `kbdio' routines will, as a
24542795Skato	 * side effect.
24642795Skato	 */
24742795Skato	s = spltty();
24842795Skato	kbd = (keyboard_t *)arg;
249175001Snyan	if (kbdd_lock(kbd, TRUE)) {
25042795Skato		/*
25142795Skato		 * We have seen the lock flag is not set. Let's reset
25242795Skato		 * the flag early, otherwise the LED update routine fails
25342795Skato		 * which may want the lock during the interrupt routine.
25442795Skato		 */
255175001Snyan		kbdd_lock(kbd, FALSE);
256175001Snyan		if (kbdd_check_char(kbd))
257175001Snyan			kbdd_intr(kbd, NULL);
25842795Skato	}
25942795Skato	splx(s);
26042795Skato	timeout(pckbd_timeout, arg, hz/10);
26142795Skato}
26242795Skato
26342795Skato/* LOW-LEVEL */
26442795Skato
265114216Skan#include <sys/limits.h>
26642795Skato
26742795Skato#define PC98KBD_DEFAULT	0
26842795Skato
26942795Skatotypedef caddr_t		KBDC;
27042795Skato
27142795Skatotypedef struct pckbd_state {
27242795Skato	KBDC		kbdc;		/* keyboard controller */
27342795Skato	int		ks_mode;	/* input mode (K_XLATE,K_RAW,K_CODE) */
27442795Skato	int		ks_flags;	/* flags */
27542795Skato#define COMPOSE		(1 << 0)
27642795Skato	int		ks_state;	/* shift/lock key state */
27742795Skato	int		ks_accents;	/* accent key index (> 0) */
27842795Skato	u_int		ks_composed_char; /* composed char code (> 0) */
27942795Skato} pckbd_state_t;
28042795Skato
28142795Skato/* keyboard driver declaration */
28242795Skatostatic int		pckbd_configure(int flags);
28342795Skatostatic kbd_probe_t	pckbd_probe;
28442795Skatostatic kbd_init_t	pckbd_init;
28542795Skatostatic kbd_term_t	pckbd_term;
28642795Skatostatic kbd_intr_t	pckbd_intr;
28742795Skatostatic kbd_test_if_t	pckbd_test_if;
28842795Skatostatic kbd_enable_t	pckbd_enable;
28942795Skatostatic kbd_disable_t	pckbd_disable;
29042795Skatostatic kbd_read_t	pckbd_read;
29142795Skatostatic kbd_check_t	pckbd_check;
29242795Skatostatic kbd_read_char_t	pckbd_read_char;
29342795Skatostatic kbd_check_char_t	pckbd_check_char;
29442795Skatostatic kbd_ioctl_t	pckbd_ioctl;
29542795Skatostatic kbd_lock_t	pckbd_lock;
29642795Skatostatic kbd_clear_state_t pckbd_clear_state;
29742795Skatostatic kbd_get_state_t	pckbd_get_state;
29842795Skatostatic kbd_set_state_t	pckbd_set_state;
29944635Skatostatic kbd_poll_mode_t	pckbd_poll;
30042795Skato
30142795Skatokeyboard_switch_t pckbdsw = {
30242795Skato	pckbd_probe,
30342795Skato	pckbd_init,
30442795Skato	pckbd_term,
30542795Skato	pckbd_intr,
30642795Skato	pckbd_test_if,
30742795Skato	pckbd_enable,
30842795Skato	pckbd_disable,
30942795Skato	pckbd_read,
31042795Skato	pckbd_check,
31142795Skato	pckbd_read_char,
31242795Skato	pckbd_check_char,
31342795Skato	pckbd_ioctl,
31442795Skato	pckbd_lock,
31542795Skato	pckbd_clear_state,
31642795Skato	pckbd_get_state,
31742795Skato	pckbd_set_state,
31842795Skato	genkbd_get_fkeystr,
31944635Skato	pckbd_poll,
32042795Skato	genkbd_diag,
32142795Skato};
32242795Skato
32342795SkatoKEYBOARD_DRIVER(pckbd, pckbdsw, pckbd_configure);
32442795Skato
32542795Skatostruct kbdc_softc {
32642795Skato    int port;			/* base port address */
32742795Skato    int lock;			/* FIXME: XXX not quite a semaphore... */
32842795Skato};
32942795Skato
33042795Skato/* local functions */
33142795Skatostatic int		probe_keyboard(KBDC kbdc, int flags);
33242795Skatostatic int		init_keyboard(KBDC kbdc, int *type, int flags);
33342795Skatostatic KBDC		kbdc_open(int port);
33442795Skatostatic int		kbdc_lock(KBDC kbdc, int lock);
33542795Skatostatic int		kbdc_data_ready(KBDC kbdc);
33642795Skatostatic int		read_kbd_data(KBDC kbdc);
33742795Skatostatic int		read_kbd_data_no_wait(KBDC kbdc);
33842795Skatostatic int		wait_for_kbd_data(struct kbdc_softc *kbdc);
33942795Skato
34042795Skato/* local variables */
34142795Skato
34242795Skato/* the initial key map, accent map and fkey strings */
343146138Snyan#include <pc98/cbus/pckbdtables.h>
34442795Skato
34542795Skato/* structures for the default keyboard */
34642795Skatostatic keyboard_t	default_kbd;
34742795Skatostatic pckbd_state_t	default_kbd_state;
34842795Skatostatic keymap_t		default_keymap;
34942795Skatostatic accentmap_t	default_accentmap;
35042795Skatostatic fkeytab_t	default_fkeytab[NUM_FKEYS];
35142795Skato
35242795Skato/*
35342795Skato * The back door to the keyboard driver!
35442795Skato * This function is called by the console driver, via the kbdio module,
35542795Skato * to tickle keyboard drivers when the low-level console is being initialized.
35642795Skato * Almost nothing in the kernel has been initialied yet.  Try to probe
35742795Skato * keyboards if possible.
35842795Skato * NOTE: because of the way the low-level conole is initialized, this routine
35942795Skato * may be called more than once!!
36042795Skato */
36142795Skatostatic int
36242795Skatopckbd_configure(int flags)
36342795Skato{
36442795Skato	keyboard_t *kbd;
36542795Skato	int arg[2];
36644635Skato	int i;
36742795Skato
36842795Skato	/* XXX: a kludge to obtain the device configuration flags */
36945783Skato	if (resource_int_value(DRIVER_NAME, 0, "flags", &i) == 0) {
37045783Skato		flags |= i;
37144635Skato		/* if the driver is disabled, unregister the keyboard if any */
372117167Sjhb		if (resource_disabled(DRIVER_NAME, 0)) {
37344635Skato			i = kbd_find_keyboard(DRIVER_NAME, PC98KBD_DEFAULT);
37444635Skato			if (i >= 0) {
37544635Skato				kbd = kbd_get_keyboard(i);
37644635Skato				kbd_unregister(kbd);
37744635Skato				kbd->kb_flags &= ~KB_REGISTERED;
37844635Skato				return 0;
37944635Skato			}
38044635Skato		}
38144635Skato	}
38242795Skato
38342795Skato	/* probe the default keyboard */
38442795Skato	arg[0] = -1;
38542795Skato	arg[1] = -1;
38644635Skato	kbd = NULL;
38744635Skato	if (pckbd_probe(PC98KBD_DEFAULT, arg, flags))
38842795Skato		return 0;
38944635Skato	if (pckbd_init(PC98KBD_DEFAULT, &kbd, arg, flags))
39044635Skato		return 0;
39142795Skato
39244635Skato	/* return the number of found keyboards */
39344635Skato	return 1;
39442795Skato}
39542795Skato
39642795Skato/* low-level functions */
39742795Skato
39844635Skato/* detect a keyboard */
39942795Skatostatic int
40044635Skatopckbd_probe(int unit, void *arg, int flags)
40142795Skato{
40242795Skato	KBDC kbdc;
40342795Skato	int *data = (int *)arg;
40442795Skato
40542795Skato	if (unit != PC98KBD_DEFAULT)
40642795Skato		return ENXIO;
40744635Skato	if (KBD_IS_PROBED(&default_kbd))
40842795Skato		return 0;
40942795Skato
41044635Skato	kbdc = kbdc_open(data[0]);
41142795Skato	if (kbdc == NULL)
41242795Skato		return ENXIO;
41342795Skato	if (probe_keyboard(kbdc, flags)) {
41442795Skato		if (flags & KB_CONF_FAIL_IF_NO_KBD)
41542795Skato			return ENXIO;
41642795Skato	}
41742795Skato	return 0;
41842795Skato}
41942795Skato
42042795Skato/* reset and initialize the device */
42142795Skatostatic int
42244635Skatopckbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
42342795Skato{
42444635Skato	keyboard_t *kbd;
42544635Skato	pckbd_state_t *state;
42644635Skato	keymap_t *keymap;
42744635Skato	accentmap_t *accmap;
42844635Skato	fkeytab_t *fkeymap;
42944635Skato	int fkeymap_size;
43044635Skato	int *data = (int *)arg;
43142795Skato
43244635Skato	if (unit != PC98KBD_DEFAULT)			/* shouldn't happen */
43344635Skato		return ENXIO;
43442795Skato
43544635Skato	*kbdp = kbd = &default_kbd;
43644635Skato	state = &default_kbd_state;
43744635Skato	if (!KBD_IS_PROBED(kbd)) {
43844635Skato		keymap = &default_keymap;
43944635Skato		accmap = &default_accentmap;
44044635Skato		fkeymap = default_fkeytab;
44144635Skato		fkeymap_size =
44244635Skato			sizeof(default_fkeytab)/sizeof(default_fkeytab[0]);
44344635Skato
44444635Skato		state->kbdc = kbdc_open(data[0]);
44544635Skato		if (state->kbdc == NULL)
44644635Skato			return ENXIO;
44744635Skato		kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags,
44844635Skato				data[0], IO_KBDSIZE);
44944635Skato		bcopy(&key_map, keymap, sizeof(key_map));
45044635Skato		bcopy(&accent_map, accmap, sizeof(accent_map));
45144635Skato		bcopy(fkey_tab, fkeymap,
45244635Skato		      imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
45344635Skato		kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
45444635Skato		kbd->kb_data = (void *)state;
45544635Skato
45644635Skato		if (probe_keyboard(state->kbdc, flags)) {/* shouldn't happen */
45744635Skato			if (flags & KB_CONF_FAIL_IF_NO_KBD)
45844635Skato				return ENXIO;
45944635Skato		} else {
46044635Skato			KBD_FOUND_DEVICE(kbd);
46144635Skato		}
46244635Skato		pckbd_clear_state(kbd);
46344635Skato		state->ks_mode = K_XLATE;
46444635Skato		KBD_PROBE_DONE(kbd);
46544635Skato	}
46644635Skato	if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
46742795Skato		if (KBD_HAS_DEVICE(kbd)
46844635Skato		    && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config)
46942795Skato		    && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD))
47042795Skato			return ENXIO;
47144635Skato		pckbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
47242795Skato		KBD_INIT_DONE(kbd);
47342795Skato	}
47442795Skato	if (!KBD_IS_CONFIGURED(kbd)) {
47542795Skato		if (kbd_register(kbd) < 0)
47642795Skato			return ENXIO;
47742795Skato		KBD_CONFIG_DONE(kbd);
47842795Skato	}
47942795Skato
48042795Skato	return 0;
48142795Skato}
48242795Skato
48342795Skato/* finish using this keyboard */
48442795Skatostatic int
48542795Skatopckbd_term(keyboard_t *kbd)
48642795Skato{
48742795Skato	kbd_unregister(kbd);
48842795Skato	return 0;
48942795Skato}
49042795Skato
49142795Skato/* keyboard interrupt routine */
49242795Skatostatic int
49344635Skatopckbd_intr(keyboard_t *kbd, void *arg)
49442795Skato{
49542795Skato	int c;
49642795Skato
49742795Skato	if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
49842795Skato		/* let the callback function to process the input */
49942795Skato		(*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
50042795Skato					    kbd->kb_callback.kc_arg);
50142795Skato	} else {
50242795Skato		/* read and discard the input; no one is waiting for input */
50342795Skato		do {
50442795Skato			c = pckbd_read_char(kbd, FALSE);
50542795Skato		} while (c != NOKEY);
50642795Skato	}
50742795Skato	return 0;
50842795Skato}
50942795Skato
51042795Skato/* test the interface to the device */
51142795Skatostatic int
51242795Skatopckbd_test_if(keyboard_t *kbd)
51342795Skato{
51442795Skato	return 0;
51542795Skato}
51642795Skato
51742795Skato/*
51842795Skato * Enable the access to the device; until this function is called,
51942795Skato * the client cannot read from the keyboard.
52042795Skato */
52142795Skatostatic int
52242795Skatopckbd_enable(keyboard_t *kbd)
52342795Skato{
52442795Skato	int s;
52542795Skato
52642795Skato	s = spltty();
52742795Skato	KBD_ACTIVATE(kbd);
52842795Skato	splx(s);
52942795Skato	return 0;
53042795Skato}
53142795Skato
53242795Skato/* disallow the access to the device */
53342795Skatostatic int
53442795Skatopckbd_disable(keyboard_t *kbd)
53542795Skato{
53642795Skato	int s;
53742795Skato
53842795Skato	s = spltty();
53942795Skato	KBD_DEACTIVATE(kbd);
54042795Skato	splx(s);
54142795Skato	return 0;
54242795Skato}
54342795Skato
54442795Skato/* read one byte from the keyboard if it's allowed */
54542795Skatostatic int
54642795Skatopckbd_read(keyboard_t *kbd, int wait)
54742795Skato{
54842795Skato	int c;
54942795Skato
55042795Skato	if (wait)
55142795Skato		c = read_kbd_data(((pckbd_state_t *)kbd->kb_data)->kbdc);
55242795Skato	else
55342795Skato		c = read_kbd_data_no_wait(((pckbd_state_t *)kbd->kb_data)->kbdc);
55454551Skato	if (c != -1)
55554551Skato		++kbd->kb_count;
55642795Skato	return (KBD_IS_ACTIVE(kbd) ? c : -1);
55742795Skato}
55842795Skato
55942795Skato/* check if data is waiting */
56042795Skatostatic int
56142795Skatopckbd_check(keyboard_t *kbd)
56242795Skato{
56342795Skato	if (!KBD_IS_ACTIVE(kbd))
56442795Skato		return FALSE;
56542795Skato	return kbdc_data_ready(((pckbd_state_t *)kbd->kb_data)->kbdc);
56642795Skato}
56742795Skato
56842795Skato/* read char from the keyboard */
56942795Skatostatic u_int
57042795Skatopckbd_read_char(keyboard_t *kbd, int wait)
57142795Skato{
57242795Skato	pckbd_state_t *state;
57342795Skato	u_int action;
57442795Skato	int scancode;
57542795Skato	int keycode;
57642795Skato
57742795Skato	state = (pckbd_state_t *)kbd->kb_data;
57842795Skatonext_code:
57942795Skato	/* do we have a composed char to return? */
58042795Skato	if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
58142795Skato		action = state->ks_composed_char;
58242795Skato		state->ks_composed_char = 0;
58342795Skato		if (action > UCHAR_MAX)
58442795Skato			return ERRKEY;
58542795Skato		return action;
58642795Skato	}
58742795Skato
58842795Skato	/* see if there is something in the keyboard port */
58942795Skato	if (wait) {
59042795Skato		do {
59142795Skato			scancode = read_kbd_data(state->kbdc);
59242795Skato		} while (scancode == -1);
59342795Skato	} else {
59442795Skato		scancode = read_kbd_data_no_wait(state->kbdc);
59542795Skato		if (scancode == -1)
59642795Skato			return NOKEY;
59742795Skato	}
59854551Skato	++kbd->kb_count;
59942795Skato
60054551Skato#if 0
60154551Skato	printf("pckbd_read_char(): scancode:0x%x\n", scancode);
60254551Skato#endif
60354551Skato
60442795Skato	/* return the byte as is for the K_RAW mode */
60542795Skato	if (state->ks_mode == K_RAW)
60642795Skato		return scancode;
60742795Skato
60842795Skato	/* translate the scan code into a keycode */
60942795Skato	keycode = scancode & 0x7F;
61042795Skato	switch(scancode) {
61142795Skato	case 0xF3:	/* GRPH (compose key) released */
61242795Skato		if (state->ks_flags & COMPOSE) {
61342795Skato			state->ks_flags &= ~COMPOSE;
61442795Skato			if (state->ks_composed_char > UCHAR_MAX)
61542795Skato				state->ks_composed_char = 0;
61642795Skato		}
61742795Skato		break;
61842795Skato	case 0x73:	/* GRPH (compose key) pressed */
61942795Skato		if (!(state->ks_flags & COMPOSE)) {
62042795Skato			state->ks_flags |= COMPOSE;
62142795Skato			state->ks_composed_char = 0;
62242795Skato		}
62342795Skato		break;
62442795Skato	}
62542795Skato
62642795Skato	/* return the key code in the K_CODE mode */
62742795Skato	if (state->ks_mode == K_CODE)
62842795Skato		return (keycode | (scancode & 0x80));
62942795Skato
63042795Skato	/* compose a character code */
63142795Skato	if (state->ks_flags & COMPOSE) {
63242795Skato		switch (scancode) {
63342795Skato		/* key pressed, process it */
63442795Skato		case 0x42: case 0x43: case 0x44:	/* keypad 7,8,9 */
63542795Skato			state->ks_composed_char *= 10;
63642795Skato			state->ks_composed_char += scancode - 0x3B;
63742795Skato			if (state->ks_composed_char > UCHAR_MAX)
63842795Skato				return ERRKEY;
63942795Skato			goto next_code;
64042795Skato		case 0x46: case 0x47: case 0x48:	/* keypad 4,5,6 */
64142795Skato			state->ks_composed_char *= 10;
64242795Skato			state->ks_composed_char += scancode - 0x42;
64342795Skato			if (state->ks_composed_char > UCHAR_MAX)
64442795Skato				return ERRKEY;
64542795Skato			goto next_code;
64642795Skato		case 0x4A: case 0x4B: case 0x4C:	/* keypad 1,2,3 */
64742795Skato			state->ks_composed_char *= 10;
64842795Skato			state->ks_composed_char += scancode - 0x49;
64942795Skato			if (state->ks_composed_char > UCHAR_MAX)
65042795Skato				return ERRKEY;
65142795Skato			goto next_code;
65242795Skato		case 0x4E:				/* keypad 0 */
65342795Skato			state->ks_composed_char *= 10;
65442795Skato			if (state->ks_composed_char > UCHAR_MAX)
65542795Skato				return ERRKEY;
65642795Skato			goto next_code;
65742795Skato
65842795Skato		/* key released, no interest here */
65942795Skato		case 0xC2: case 0xC3: case 0xC4:	/* keypad 7,8,9 */
66042795Skato		case 0xC6: case 0xC7: case 0xC8:	/* keypad 4,5,6 */
66142795Skato		case 0xCA: case 0xCB: case 0xCC:	/* keypad 1,2,3 */
66242795Skato		case 0xCE:				/* keypad 0 */
66342795Skato			goto next_code;
66442795Skato
66542795Skato		case 0x73:				/* GRPH key */
66642795Skato			break;
66742795Skato
66842795Skato		default:
66942795Skato			if (state->ks_composed_char > 0) {
67042795Skato				state->ks_flags &= ~COMPOSE;
67142795Skato				state->ks_composed_char = 0;
67242795Skato				return ERRKEY;
67342795Skato			}
67442795Skato			break;
67542795Skato		}
67642795Skato	}
67742795Skato
67842795Skato	/* keycode to key action */
67942795Skato	action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
68042795Skato				  &state->ks_state, &state->ks_accents);
68142795Skato	if (action == NOKEY)
68242795Skato		goto next_code;
68342795Skato	else
68442795Skato		return action;
68542795Skato}
68642795Skato
68742795Skato/* check if char is waiting */
68842795Skatostatic int
68942795Skatopckbd_check_char(keyboard_t *kbd)
69042795Skato{
69142795Skato	pckbd_state_t *state;
69242795Skato
69342795Skato	if (!KBD_IS_ACTIVE(kbd))
69442795Skato		return FALSE;
69542795Skato	state = (pckbd_state_t *)kbd->kb_data;
69642795Skato	if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
69742795Skato		return TRUE;
69842795Skato	return kbdc_data_ready(state->kbdc);
69942795Skato}
70042795Skato
70142795Skato/* some useful control functions */
70242795Skatostatic int
70342795Skatopckbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
70442795Skato{
70542795Skato	pckbd_state_t *state = kbd->kb_data;
70642795Skato	int s;
70742795Skato	int i;
708162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
709162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
710162711Sru	int ival;
711162711Sru#endif
71242795Skato
71342795Skato	s = spltty();
71442795Skato	switch (cmd) {
71542795Skato
71642795Skato	case KDGKBMODE:		/* get keyboard mode */
71742795Skato		*(int *)arg = state->ks_mode;
71842795Skato		break;
719162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
720162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
721162711Sru	case _IO('K', 7):
722162711Sru		ival = IOCPARM_IVAL(arg);
723162711Sru		arg = (caddr_t)&ival;
724162711Sru		/* FALLTHROUGH */
725162711Sru#endif
72642795Skato	case KDSKBMODE:		/* set keyboard mode */
72742795Skato		switch (*(int *)arg) {
72842795Skato		case K_XLATE:
72942795Skato			if (state->ks_mode != K_XLATE) {
73042795Skato				/* make lock key state and LED state match */
73142795Skato				state->ks_state &= ~LOCK_MASK;
73242795Skato				state->ks_state |= KBD_LED_VAL(kbd);
73342795Skato			}
734102412Scharnier			/* FALLTHROUGH */
73542795Skato		case K_RAW:
73642795Skato		case K_CODE:
73742795Skato			if (state->ks_mode != *(int *)arg) {
73842795Skato				pckbd_clear_state(kbd);
73942795Skato				state->ks_mode = *(int *)arg;
74042795Skato			}
74142795Skato			break;
74242795Skato		default:
74342795Skato			splx(s);
74442795Skato			return EINVAL;
74542795Skato		}
74642795Skato		break;
74742795Skato
74842795Skato	case KDGETLED:		/* get keyboard LED */
74942795Skato		*(int *)arg = KBD_LED_VAL(kbd);
75042795Skato		break;
751162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
752162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
753162711Sru	case _IO('K', 66):
754162711Sru		ival = IOCPARM_IVAL(arg);
755162711Sru		arg = (caddr_t)&ival;
756162711Sru		/* FALLTHROUGH */
757162711Sru#endif
75842795Skato	case KDSETLED:		/* set keyboard LED */
75942795Skato		/* NOTE: lock key state in ks_state won't be changed */
76042795Skato		if (*(int *)arg & ~LOCK_MASK) {
76142795Skato			splx(s);
76242795Skato			return EINVAL;
76342795Skato		}
76442795Skato		i = *(int *)arg;
76542795Skato		/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
76642795Skato		if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
76742795Skato			if (i & ALKED)
76842795Skato				i |= CLKED;
76942795Skato			else
77042795Skato				i &= ~CLKED;
77142795Skato		}
77242795Skato		KBD_LED_VAL(kbd) = *(int *)arg;
77342795Skato		break;
77442795Skato
77542795Skato	case KDGKBSTATE:	/* get lock key state */
77642795Skato		*(int *)arg = state->ks_state & LOCK_MASK;
77742795Skato		break;
778162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
779162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
780162711Sru	case _IO('K', 20):
781162711Sru		ival = IOCPARM_IVAL(arg);
782162711Sru		arg = (caddr_t)&ival;
783162711Sru		/* FALLTHROUGH */
784162711Sru#endif
78542795Skato	case KDSKBSTATE:	/* set lock key state */
78642795Skato		if (*(int *)arg & ~LOCK_MASK) {
78742795Skato			splx(s);
78842795Skato			return EINVAL;
78942795Skato		}
79042795Skato		state->ks_state &= ~LOCK_MASK;
79142795Skato		state->ks_state |= *(int *)arg;
79242795Skato		splx(s);
79342795Skato		/* set LEDs and quit */
79442795Skato		return pckbd_ioctl(kbd, KDSETLED, arg);
79542795Skato
79644635Skato	case KDSETRAD:		/* set keyboard repeat rate (old interface)*/
79742795Skato		break;
79844635Skato	case KDSETREPEAT:	/* set keyboard repeat rate (new interface) */
79944635Skato		break;
80042795Skato
80142795Skato	case PIO_KEYMAP:	/* set keyboard translation table */
802224126Sed	case OPIO_KEYMAP:	/* set keyboard translation table (compat) */
80342795Skato	case PIO_KEYMAPENT:	/* set keyboard translation table entry */
80442795Skato	case PIO_DEADKEYMAP:	/* set accent key translation table */
80542795Skato		state->ks_accents = 0;
806102412Scharnier		/* FALLTHROUGH */
80742795Skato	default:
80842795Skato		splx(s);
80942795Skato		return genkbd_commonioctl(kbd, cmd, arg);
81042795Skato	}
81142795Skato
81242795Skato	splx(s);
81342795Skato	return 0;
81442795Skato}
81542795Skato
81642795Skato/* lock the access to the keyboard */
81742795Skatostatic int
81842795Skatopckbd_lock(keyboard_t *kbd, int lock)
81942795Skato{
82042795Skato	return kbdc_lock(((pckbd_state_t *)kbd->kb_data)->kbdc, lock);
82142795Skato}
82242795Skato
82342795Skato/* clear the internal state of the keyboard */
82442795Skatostatic void
82542795Skatopckbd_clear_state(keyboard_t *kbd)
82642795Skato{
82742795Skato	pckbd_state_t *state;
82842795Skato
82942795Skato	state = (pckbd_state_t *)kbd->kb_data;
83042795Skato	state->ks_flags = 0;
83142795Skato	state->ks_state &= LOCK_MASK;	/* preserve locking key state */
83242795Skato	state->ks_accents = 0;
83342795Skato	state->ks_composed_char = 0;
83442795Skato}
83542795Skato
83642795Skato/* save the internal state */
83742795Skatostatic int
83842795Skatopckbd_get_state(keyboard_t *kbd, void *buf, size_t len)
83942795Skato{
84042795Skato	if (len == 0)
84142795Skato		return sizeof(pckbd_state_t);
84242795Skato	if (len < sizeof(pckbd_state_t))
84342795Skato		return -1;
84442795Skato	bcopy(kbd->kb_data, buf, sizeof(pckbd_state_t));
84542795Skato	return 0;
84642795Skato}
84742795Skato
84842795Skato/* set the internal state */
84942795Skatostatic int
85042795Skatopckbd_set_state(keyboard_t *kbd, void *buf, size_t len)
85142795Skato{
85242795Skato	if (len < sizeof(pckbd_state_t))
85342795Skato		return ENOMEM;
85442795Skato	if (((pckbd_state_t *)kbd->kb_data)->kbdc
85542795Skato		!= ((pckbd_state_t *)buf)->kbdc)
85642795Skato		return ENOMEM;
85742795Skato	bcopy(buf, kbd->kb_data, sizeof(pckbd_state_t));
85842795Skato	return 0;
85942795Skato}
86042795Skato
86144635Skato/* set polling mode */
86244635Skatostatic int
86344635Skatopckbd_poll(keyboard_t *kbd, int on)
86444635Skato{
86544635Skato	return 0;
86644635Skato}
86744635Skato
86842795Skato/* local functions */
86942795Skato
87042795Skatostatic int
87142795Skatoprobe_keyboard(KBDC kbdc, int flags)
87242795Skato{
87342795Skato	return 0;
87442795Skato}
87542795Skato
87642795Skatostatic int
87742795Skatoinit_keyboard(KBDC kbdc, int *type, int flags)
87842795Skato{
87942795Skato	*type = KB_OTHER;
88042795Skato	return 0;
88142795Skato}
88242795Skato
88342795Skato/* keyboard I/O routines */
88442795Skato
88542795Skato/* retry count */
88642795Skato#ifndef KBD_MAXRETRY
88742795Skato#define KBD_MAXRETRY	3
88842795Skato#endif
88942795Skato
89042795Skato/* timing parameters */
89142795Skato#ifndef KBD_RESETDELAY
89242795Skato#define KBD_RESETDELAY  200     /* wait 200msec after kbd/mouse reset */
89342795Skato#endif
89442795Skato#ifndef KBD_MAXWAIT
89542795Skato#define KBD_MAXWAIT	5 	/* wait 5 times at most after reset */
89642795Skato#endif
89742795Skato
89842795Skato/* I/O recovery time */
89942795Skato#define KBDC_DELAYTIME	37
90042795Skato#define KBDD_DELAYTIME	37
90142795Skato
90242795Skato/* I/O ports */
90342795Skato#define KBD_STATUS_PORT 	2	/* status port, read */
90442795Skato#define KBD_DATA_PORT		0	/* data port, read */
90542795Skato
90642795Skato/* status bits (KBD_STATUS_PORT) */
90742795Skato#define KBDS_BUFFER_FULL	0x0002
90842795Skato
90942795Skato/* macros */
91042795Skato
91142795Skato#define kbdcp(p)		((struct kbdc_softc *)(p))
91242795Skato
91342795Skato/* local variables */
91442795Skato
915102151Speterstatic struct kbdc_softc kbdc_softc[1] = { { 0 }, };
91642795Skato
91742795Skato/* associate a port number with a KBDC */
91842795Skato
91942795Skatostatic KBDC
92042795Skatokbdc_open(int port)
92142795Skato{
92242795Skato	if (port <= 0)
92342795Skato		port = IO_KBD;
92442795Skato
925102151Speter	/* PC-98 has only one keyboard I/F */
926102151Speter	kbdc_softc[0].port = port;
927102151Speter	kbdc_softc[0].lock = FALSE;
928102151Speter	return (KBDC)&kbdc_softc[0];
92942795Skato}
93042795Skato
93142795Skato/* set/reset polling lock */
93242795Skatostatic int
93342795Skatokbdc_lock(KBDC p, int lock)
93442795Skato{
93542795Skato    int prevlock;
93642795Skato
93742795Skato    prevlock = kbdcp(p)->lock;
93842795Skato    kbdcp(p)->lock = lock;
93942795Skato
94042795Skato    return (prevlock != lock);
94142795Skato}
94242795Skato
94342795Skato/* check if any data is waiting to be processed */
94442795Skatostatic int
94542795Skatokbdc_data_ready(KBDC p)
94642795Skato{
94742795Skato	return (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL);
94842795Skato}
94942795Skato
95042795Skato/* wait for data from the keyboard */
95142795Skatostatic int
95242795Skatowait_for_kbd_data(struct kbdc_softc *kbdc)
95342795Skato{
95442795Skato    /* CPU will stay inside the loop for 200msec at most */
95542795Skato    int retry = 10000;
95642795Skato    int port = kbdc->port;
95742795Skato
95842795Skato    while (!(inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)) {
95942795Skato	DELAY(KBDD_DELAYTIME);
96042795Skato	DELAY(KBDC_DELAYTIME);
96142795Skato        if (--retry < 0)
96242795Skato    	    return 0;
96342795Skato    }
96442795Skato    DELAY(KBDD_DELAYTIME);
96542795Skato    return 1;
96642795Skato}
96742795Skato
96842795Skato/* read one byte from the keyboard */
96942795Skatostatic int
97042795Skatoread_kbd_data(KBDC p)
97142795Skato{
97242795Skato    if (!wait_for_kbd_data(kbdcp(p)))
97342795Skato        return -1;		/* timeout */
97442795Skato    DELAY(KBDC_DELAYTIME);
97542795Skato    return inb(kbdcp(p)->port + KBD_DATA_PORT);
97642795Skato}
97742795Skato
97842795Skato/* read one byte from the keyboard, but return immediately if
97942795Skato * no data is waiting
98042795Skato */
98142795Skatostatic int
98242795Skatoread_kbd_data_no_wait(KBDC p)
98342795Skato{
98442795Skato    if (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) {
98542795Skato        DELAY(KBDD_DELAYTIME);
98642795Skato        return inb(kbdcp(p)->port + KBD_DATA_PORT);
98742795Skato    }
98842795Skato    return -1;		/* no data */
98942795Skato}
990