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
80245317Simpstatic int		pckbd_probe_unit(device_t dev, int port, int irq,
8144635Skato					 int flags);
82245317Simpstatic int		pckbd_attach_unit(device_t dev, 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
106245317Simp	error = pckbd_probe_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
131245317Simp	error = pckbd_attach_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
167245317Simppckbd_probe_unit(device_t dev, 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;
179245317Simp	error = (*sw->probe)(device_get_unit(dev), args, flags);
18044635Skato	if (error)
18144635Skato		return error;
18244635Skato	return 0;
18342795Skato}
18442795Skato
18542795Skatostatic int
186245317Simppckbd_attach_unit(device_t dev, keyboard_t **kbd, int port, int irq, int flags)
18742795Skato{
18842795Skato	keyboard_switch_t *sw;
18944635Skato	int args[2];
19042795Skato	int error;
191245317Simp	int unit;
19242795Skato
19342795Skato	sw = kbd_get_switch(DRIVER_NAME);
19442795Skato	if (sw == NULL)
19542795Skato		return ENXIO;
19642795Skato
19742795Skato	/* reset, initialize and enable the device */
198245317Simp	unit = device_get_unit(dev);
19944635Skato	args[0] = port;
20044635Skato	args[1] = irq;
20150236Skato	*kbd = NULL;
20244635Skato	error = (*sw->probe)(unit, args, flags);
20342795Skato	if (error)
20444635Skato		return error;
20550236Skato	error = (*sw->init)(unit, kbd, args, flags);
20644635Skato	if (error)
20744635Skato		return error;
20850236Skato	(*sw->enable)(*kbd);
20942795Skato
21042795Skato#ifdef KBD_INSTALL_CDEV
21142795Skato	/* attach a virtual keyboard cdev */
21250236Skato	error = kbd_attach(*kbd);
21342795Skato	if (error)
21442795Skato		return error;
21542795Skato#endif /* KBD_INSTALL_CDEV */
21642795Skato
21742795Skato	/*
21842795Skato	 * This is a kludge to compensate for lost keyboard interrupts.
21942795Skato	 * A similar code used to be in syscons. See below. XXX
22042795Skato	 */
22150236Skato	pckbd_timeout(*kbd);
22242795Skato
22342795Skato	if (bootverbose)
22450236Skato		(*sw->diag)(*kbd, bootverbose);
22542795Skato
22642795Skato	return 0;
22742795Skato}
22842795Skato
22942795Skatostatic void
23042795Skatopckbd_timeout(void *arg)
23142795Skato{
23242795Skato	keyboard_t *kbd;
23342795Skato	int s;
23442795Skato
23542795Skato	/* The following comments are extracted from syscons.c (1.287) */
23642795Skato	/*
23742795Skato	 * With release 2.1 of the Xaccel server, the keyboard is left
23842795Skato	 * hanging pretty often. Apparently an interrupt from the
23942795Skato	 * keyboard is lost, and I don't know why (yet).
24042795Skato	 * This ugly hack calls scintr if input is ready for the keyboard
24142795Skato	 * and conveniently hides the problem.			XXX
24242795Skato	 */
24342795Skato	/*
24442795Skato	 * Try removing anything stuck in the keyboard controller; whether
24542795Skato	 * it's a keyboard scan code or mouse data. `scintr()' doesn't
24642795Skato	 * read the mouse data directly, but `kbdio' routines will, as a
24742795Skato	 * side effect.
24842795Skato	 */
24942795Skato	s = spltty();
25042795Skato	kbd = (keyboard_t *)arg;
251175001Snyan	if (kbdd_lock(kbd, TRUE)) {
25242795Skato		/*
25342795Skato		 * We have seen the lock flag is not set. Let's reset
25442795Skato		 * the flag early, otherwise the LED update routine fails
25542795Skato		 * which may want the lock during the interrupt routine.
25642795Skato		 */
257175001Snyan		kbdd_lock(kbd, FALSE);
258175001Snyan		if (kbdd_check_char(kbd))
259175001Snyan			kbdd_intr(kbd, NULL);
26042795Skato	}
26142795Skato	splx(s);
26242795Skato	timeout(pckbd_timeout, arg, hz/10);
26342795Skato}
26442795Skato
26542795Skato/* LOW-LEVEL */
26642795Skato
267114216Skan#include <sys/limits.h>
26842795Skato
26942795Skato#define PC98KBD_DEFAULT	0
27042795Skato
27142795Skatotypedef caddr_t		KBDC;
27242795Skato
27342795Skatotypedef struct pckbd_state {
27442795Skato	KBDC		kbdc;		/* keyboard controller */
27542795Skato	int		ks_mode;	/* input mode (K_XLATE,K_RAW,K_CODE) */
27642795Skato	int		ks_flags;	/* flags */
27742795Skato#define COMPOSE		(1 << 0)
27842795Skato	int		ks_state;	/* shift/lock key state */
27942795Skato	int		ks_accents;	/* accent key index (> 0) */
28042795Skato	u_int		ks_composed_char; /* composed char code (> 0) */
28142795Skato} pckbd_state_t;
28242795Skato
28342795Skato/* keyboard driver declaration */
28442795Skatostatic int		pckbd_configure(int flags);
28542795Skatostatic kbd_probe_t	pckbd_probe;
28642795Skatostatic kbd_init_t	pckbd_init;
28742795Skatostatic kbd_term_t	pckbd_term;
28842795Skatostatic kbd_intr_t	pckbd_intr;
28942795Skatostatic kbd_test_if_t	pckbd_test_if;
29042795Skatostatic kbd_enable_t	pckbd_enable;
29142795Skatostatic kbd_disable_t	pckbd_disable;
29242795Skatostatic kbd_read_t	pckbd_read;
29342795Skatostatic kbd_check_t	pckbd_check;
29442795Skatostatic kbd_read_char_t	pckbd_read_char;
29542795Skatostatic kbd_check_char_t	pckbd_check_char;
29642795Skatostatic kbd_ioctl_t	pckbd_ioctl;
29742795Skatostatic kbd_lock_t	pckbd_lock;
29842795Skatostatic kbd_clear_state_t pckbd_clear_state;
29942795Skatostatic kbd_get_state_t	pckbd_get_state;
30042795Skatostatic kbd_set_state_t	pckbd_set_state;
30144635Skatostatic kbd_poll_mode_t	pckbd_poll;
30242795Skato
30342795Skatokeyboard_switch_t pckbdsw = {
30442795Skato	pckbd_probe,
30542795Skato	pckbd_init,
30642795Skato	pckbd_term,
30742795Skato	pckbd_intr,
30842795Skato	pckbd_test_if,
30942795Skato	pckbd_enable,
31042795Skato	pckbd_disable,
31142795Skato	pckbd_read,
31242795Skato	pckbd_check,
31342795Skato	pckbd_read_char,
31442795Skato	pckbd_check_char,
31542795Skato	pckbd_ioctl,
31642795Skato	pckbd_lock,
31742795Skato	pckbd_clear_state,
31842795Skato	pckbd_get_state,
31942795Skato	pckbd_set_state,
32042795Skato	genkbd_get_fkeystr,
32144635Skato	pckbd_poll,
32242795Skato	genkbd_diag,
32342795Skato};
32442795Skato
32542795SkatoKEYBOARD_DRIVER(pckbd, pckbdsw, pckbd_configure);
32642795Skato
32742795Skatostruct kbdc_softc {
32842795Skato    int port;			/* base port address */
32942795Skato    int lock;			/* FIXME: XXX not quite a semaphore... */
33042795Skato};
33142795Skato
33242795Skato/* local functions */
33342795Skatostatic int		probe_keyboard(KBDC kbdc, int flags);
33442795Skatostatic int		init_keyboard(KBDC kbdc, int *type, int flags);
33542795Skatostatic KBDC		kbdc_open(int port);
33642795Skatostatic int		kbdc_lock(KBDC kbdc, int lock);
33742795Skatostatic int		kbdc_data_ready(KBDC kbdc);
33842795Skatostatic int		read_kbd_data(KBDC kbdc);
33942795Skatostatic int		read_kbd_data_no_wait(KBDC kbdc);
34042795Skatostatic int		wait_for_kbd_data(struct kbdc_softc *kbdc);
34142795Skato
34242795Skato/* local variables */
34342795Skato
34442795Skato/* the initial key map, accent map and fkey strings */
345146138Snyan#include <pc98/cbus/pckbdtables.h>
34642795Skato
34742795Skato/* structures for the default keyboard */
34842795Skatostatic keyboard_t	default_kbd;
34942795Skatostatic pckbd_state_t	default_kbd_state;
35042795Skatostatic keymap_t		default_keymap;
35142795Skatostatic accentmap_t	default_accentmap;
35242795Skatostatic fkeytab_t	default_fkeytab[NUM_FKEYS];
35342795Skato
35442795Skato/*
35542795Skato * The back door to the keyboard driver!
35642795Skato * This function is called by the console driver, via the kbdio module,
35742795Skato * to tickle keyboard drivers when the low-level console is being initialized.
35842795Skato * Almost nothing in the kernel has been initialied yet.  Try to probe
35942795Skato * keyboards if possible.
36042795Skato * NOTE: because of the way the low-level conole is initialized, this routine
36142795Skato * may be called more than once!!
36242795Skato */
36342795Skatostatic int
36442795Skatopckbd_configure(int flags)
36542795Skato{
36642795Skato	keyboard_t *kbd;
36742795Skato	int arg[2];
36844635Skato	int i;
36942795Skato
37042795Skato	/* XXX: a kludge to obtain the device configuration flags */
37145783Skato	if (resource_int_value(DRIVER_NAME, 0, "flags", &i) == 0) {
37245783Skato		flags |= i;
37344635Skato		/* if the driver is disabled, unregister the keyboard if any */
374117167Sjhb		if (resource_disabled(DRIVER_NAME, 0)) {
37544635Skato			i = kbd_find_keyboard(DRIVER_NAME, PC98KBD_DEFAULT);
37644635Skato			if (i >= 0) {
37744635Skato				kbd = kbd_get_keyboard(i);
37844635Skato				kbd_unregister(kbd);
37944635Skato				kbd->kb_flags &= ~KB_REGISTERED;
38044635Skato				return 0;
38144635Skato			}
38244635Skato		}
38344635Skato	}
38442795Skato
38542795Skato	/* probe the default keyboard */
38642795Skato	arg[0] = -1;
38742795Skato	arg[1] = -1;
38844635Skato	kbd = NULL;
38944635Skato	if (pckbd_probe(PC98KBD_DEFAULT, arg, flags))
39042795Skato		return 0;
39144635Skato	if (pckbd_init(PC98KBD_DEFAULT, &kbd, arg, flags))
39244635Skato		return 0;
39342795Skato
39444635Skato	/* return the number of found keyboards */
39544635Skato	return 1;
39642795Skato}
39742795Skato
39842795Skato/* low-level functions */
39942795Skato
40044635Skato/* detect a keyboard */
40142795Skatostatic int
40244635Skatopckbd_probe(int unit, void *arg, int flags)
40342795Skato{
40442795Skato	KBDC kbdc;
40542795Skato	int *data = (int *)arg;
40642795Skato
40742795Skato	if (unit != PC98KBD_DEFAULT)
40842795Skato		return ENXIO;
40944635Skato	if (KBD_IS_PROBED(&default_kbd))
41042795Skato		return 0;
41142795Skato
41244635Skato	kbdc = kbdc_open(data[0]);
41342795Skato	if (kbdc == NULL)
41442795Skato		return ENXIO;
41542795Skato	if (probe_keyboard(kbdc, flags)) {
41642795Skato		if (flags & KB_CONF_FAIL_IF_NO_KBD)
41742795Skato			return ENXIO;
41842795Skato	}
41942795Skato	return 0;
42042795Skato}
42142795Skato
42242795Skato/* reset and initialize the device */
42342795Skatostatic int
42444635Skatopckbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
42542795Skato{
42644635Skato	keyboard_t *kbd;
42744635Skato	pckbd_state_t *state;
42844635Skato	keymap_t *keymap;
42944635Skato	accentmap_t *accmap;
43044635Skato	fkeytab_t *fkeymap;
43144635Skato	int fkeymap_size;
43244635Skato	int *data = (int *)arg;
43342795Skato
43444635Skato	if (unit != PC98KBD_DEFAULT)			/* shouldn't happen */
43544635Skato		return ENXIO;
43642795Skato
43744635Skato	*kbdp = kbd = &default_kbd;
43844635Skato	state = &default_kbd_state;
43944635Skato	if (!KBD_IS_PROBED(kbd)) {
44044635Skato		keymap = &default_keymap;
44144635Skato		accmap = &default_accentmap;
44244635Skato		fkeymap = default_fkeytab;
44344635Skato		fkeymap_size =
44444635Skato			sizeof(default_fkeytab)/sizeof(default_fkeytab[0]);
44544635Skato
44644635Skato		state->kbdc = kbdc_open(data[0]);
44744635Skato		if (state->kbdc == NULL)
44844635Skato			return ENXIO;
44944635Skato		kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags,
45044635Skato				data[0], IO_KBDSIZE);
45144635Skato		bcopy(&key_map, keymap, sizeof(key_map));
45244635Skato		bcopy(&accent_map, accmap, sizeof(accent_map));
45344635Skato		bcopy(fkey_tab, fkeymap,
45444635Skato		      imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
45544635Skato		kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
45644635Skato		kbd->kb_data = (void *)state;
45744635Skato
45844635Skato		if (probe_keyboard(state->kbdc, flags)) {/* shouldn't happen */
45944635Skato			if (flags & KB_CONF_FAIL_IF_NO_KBD)
46044635Skato				return ENXIO;
46144635Skato		} else {
46244635Skato			KBD_FOUND_DEVICE(kbd);
46344635Skato		}
46444635Skato		pckbd_clear_state(kbd);
46544635Skato		state->ks_mode = K_XLATE;
46644635Skato		KBD_PROBE_DONE(kbd);
46744635Skato	}
46844635Skato	if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
46942795Skato		if (KBD_HAS_DEVICE(kbd)
47044635Skato		    && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config)
47142795Skato		    && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD))
47242795Skato			return ENXIO;
47344635Skato		pckbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
47442795Skato		KBD_INIT_DONE(kbd);
47542795Skato	}
47642795Skato	if (!KBD_IS_CONFIGURED(kbd)) {
47742795Skato		if (kbd_register(kbd) < 0)
47842795Skato			return ENXIO;
47942795Skato		KBD_CONFIG_DONE(kbd);
48042795Skato	}
48142795Skato
48242795Skato	return 0;
48342795Skato}
48442795Skato
48542795Skato/* finish using this keyboard */
48642795Skatostatic int
48742795Skatopckbd_term(keyboard_t *kbd)
48842795Skato{
48942795Skato	kbd_unregister(kbd);
49042795Skato	return 0;
49142795Skato}
49242795Skato
49342795Skato/* keyboard interrupt routine */
49442795Skatostatic int
49544635Skatopckbd_intr(keyboard_t *kbd, void *arg)
49642795Skato{
49742795Skato	int c;
49842795Skato
49942795Skato	if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
50042795Skato		/* let the callback function to process the input */
50142795Skato		(*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
50242795Skato					    kbd->kb_callback.kc_arg);
50342795Skato	} else {
50442795Skato		/* read and discard the input; no one is waiting for input */
50542795Skato		do {
50642795Skato			c = pckbd_read_char(kbd, FALSE);
50742795Skato		} while (c != NOKEY);
50842795Skato	}
50942795Skato	return 0;
51042795Skato}
51142795Skato
51242795Skato/* test the interface to the device */
51342795Skatostatic int
51442795Skatopckbd_test_if(keyboard_t *kbd)
51542795Skato{
51642795Skato	return 0;
51742795Skato}
51842795Skato
51942795Skato/*
52042795Skato * Enable the access to the device; until this function is called,
52142795Skato * the client cannot read from the keyboard.
52242795Skato */
52342795Skatostatic int
52442795Skatopckbd_enable(keyboard_t *kbd)
52542795Skato{
52642795Skato	int s;
52742795Skato
52842795Skato	s = spltty();
52942795Skato	KBD_ACTIVATE(kbd);
53042795Skato	splx(s);
53142795Skato	return 0;
53242795Skato}
53342795Skato
53442795Skato/* disallow the access to the device */
53542795Skatostatic int
53642795Skatopckbd_disable(keyboard_t *kbd)
53742795Skato{
53842795Skato	int s;
53942795Skato
54042795Skato	s = spltty();
54142795Skato	KBD_DEACTIVATE(kbd);
54242795Skato	splx(s);
54342795Skato	return 0;
54442795Skato}
54542795Skato
54642795Skato/* read one byte from the keyboard if it's allowed */
54742795Skatostatic int
54842795Skatopckbd_read(keyboard_t *kbd, int wait)
54942795Skato{
55042795Skato	int c;
55142795Skato
55242795Skato	if (wait)
55342795Skato		c = read_kbd_data(((pckbd_state_t *)kbd->kb_data)->kbdc);
55442795Skato	else
55542795Skato		c = read_kbd_data_no_wait(((pckbd_state_t *)kbd->kb_data)->kbdc);
55654551Skato	if (c != -1)
55754551Skato		++kbd->kb_count;
55842795Skato	return (KBD_IS_ACTIVE(kbd) ? c : -1);
55942795Skato}
56042795Skato
56142795Skato/* check if data is waiting */
56242795Skatostatic int
56342795Skatopckbd_check(keyboard_t *kbd)
56442795Skato{
56542795Skato	if (!KBD_IS_ACTIVE(kbd))
56642795Skato		return FALSE;
56742795Skato	return kbdc_data_ready(((pckbd_state_t *)kbd->kb_data)->kbdc);
56842795Skato}
56942795Skato
57042795Skato/* read char from the keyboard */
57142795Skatostatic u_int
57242795Skatopckbd_read_char(keyboard_t *kbd, int wait)
57342795Skato{
57442795Skato	pckbd_state_t *state;
57542795Skato	u_int action;
57642795Skato	int scancode;
57742795Skato	int keycode;
57842795Skato
57942795Skato	state = (pckbd_state_t *)kbd->kb_data;
58042795Skatonext_code:
58142795Skato	/* do we have a composed char to return? */
58242795Skato	if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
58342795Skato		action = state->ks_composed_char;
58442795Skato		state->ks_composed_char = 0;
58542795Skato		if (action > UCHAR_MAX)
58642795Skato			return ERRKEY;
58742795Skato		return action;
58842795Skato	}
58942795Skato
59042795Skato	/* see if there is something in the keyboard port */
59142795Skato	if (wait) {
59242795Skato		do {
59342795Skato			scancode = read_kbd_data(state->kbdc);
59442795Skato		} while (scancode == -1);
59542795Skato	} else {
59642795Skato		scancode = read_kbd_data_no_wait(state->kbdc);
59742795Skato		if (scancode == -1)
59842795Skato			return NOKEY;
59942795Skato	}
60054551Skato	++kbd->kb_count;
60142795Skato
60254551Skato#if 0
60354551Skato	printf("pckbd_read_char(): scancode:0x%x\n", scancode);
60454551Skato#endif
60554551Skato
60642795Skato	/* return the byte as is for the K_RAW mode */
60742795Skato	if (state->ks_mode == K_RAW)
60842795Skato		return scancode;
60942795Skato
61042795Skato	/* translate the scan code into a keycode */
61142795Skato	keycode = scancode & 0x7F;
61242795Skato	switch(scancode) {
61342795Skato	case 0xF3:	/* GRPH (compose key) released */
61442795Skato		if (state->ks_flags & COMPOSE) {
61542795Skato			state->ks_flags &= ~COMPOSE;
61642795Skato			if (state->ks_composed_char > UCHAR_MAX)
61742795Skato				state->ks_composed_char = 0;
61842795Skato		}
61942795Skato		break;
62042795Skato	case 0x73:	/* GRPH (compose key) pressed */
62142795Skato		if (!(state->ks_flags & COMPOSE)) {
62242795Skato			state->ks_flags |= COMPOSE;
62342795Skato			state->ks_composed_char = 0;
62442795Skato		}
62542795Skato		break;
62642795Skato	}
62742795Skato
62842795Skato	/* return the key code in the K_CODE mode */
62942795Skato	if (state->ks_mode == K_CODE)
63042795Skato		return (keycode | (scancode & 0x80));
63142795Skato
63242795Skato	/* compose a character code */
63342795Skato	if (state->ks_flags & COMPOSE) {
63442795Skato		switch (scancode) {
63542795Skato		/* key pressed, process it */
63642795Skato		case 0x42: case 0x43: case 0x44:	/* keypad 7,8,9 */
63742795Skato			state->ks_composed_char *= 10;
63842795Skato			state->ks_composed_char += scancode - 0x3B;
63942795Skato			if (state->ks_composed_char > UCHAR_MAX)
64042795Skato				return ERRKEY;
64142795Skato			goto next_code;
64242795Skato		case 0x46: case 0x47: case 0x48:	/* keypad 4,5,6 */
64342795Skato			state->ks_composed_char *= 10;
64442795Skato			state->ks_composed_char += scancode - 0x42;
64542795Skato			if (state->ks_composed_char > UCHAR_MAX)
64642795Skato				return ERRKEY;
64742795Skato			goto next_code;
64842795Skato		case 0x4A: case 0x4B: case 0x4C:	/* keypad 1,2,3 */
64942795Skato			state->ks_composed_char *= 10;
65042795Skato			state->ks_composed_char += scancode - 0x49;
65142795Skato			if (state->ks_composed_char > UCHAR_MAX)
65242795Skato				return ERRKEY;
65342795Skato			goto next_code;
65442795Skato		case 0x4E:				/* keypad 0 */
65542795Skato			state->ks_composed_char *= 10;
65642795Skato			if (state->ks_composed_char > UCHAR_MAX)
65742795Skato				return ERRKEY;
65842795Skato			goto next_code;
65942795Skato
66042795Skato		/* key released, no interest here */
66142795Skato		case 0xC2: case 0xC3: case 0xC4:	/* keypad 7,8,9 */
66242795Skato		case 0xC6: case 0xC7: case 0xC8:	/* keypad 4,5,6 */
66342795Skato		case 0xCA: case 0xCB: case 0xCC:	/* keypad 1,2,3 */
66442795Skato		case 0xCE:				/* keypad 0 */
66542795Skato			goto next_code;
66642795Skato
66742795Skato		case 0x73:				/* GRPH key */
66842795Skato			break;
66942795Skato
67042795Skato		default:
67142795Skato			if (state->ks_composed_char > 0) {
67242795Skato				state->ks_flags &= ~COMPOSE;
67342795Skato				state->ks_composed_char = 0;
67442795Skato				return ERRKEY;
67542795Skato			}
67642795Skato			break;
67742795Skato		}
67842795Skato	}
67942795Skato
68042795Skato	/* keycode to key action */
68142795Skato	action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
68242795Skato				  &state->ks_state, &state->ks_accents);
68342795Skato	if (action == NOKEY)
68442795Skato		goto next_code;
68542795Skato	else
68642795Skato		return action;
68742795Skato}
68842795Skato
68942795Skato/* check if char is waiting */
69042795Skatostatic int
69142795Skatopckbd_check_char(keyboard_t *kbd)
69242795Skato{
69342795Skato	pckbd_state_t *state;
69442795Skato
69542795Skato	if (!KBD_IS_ACTIVE(kbd))
69642795Skato		return FALSE;
69742795Skato	state = (pckbd_state_t *)kbd->kb_data;
69842795Skato	if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
69942795Skato		return TRUE;
70042795Skato	return kbdc_data_ready(state->kbdc);
70142795Skato}
70242795Skato
70342795Skato/* some useful control functions */
70442795Skatostatic int
70542795Skatopckbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
70642795Skato{
70742795Skato	pckbd_state_t *state = kbd->kb_data;
70842795Skato	int s;
70942795Skato	int i;
710162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
711162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
712162711Sru	int ival;
713162711Sru#endif
71442795Skato
71542795Skato	s = spltty();
71642795Skato	switch (cmd) {
71742795Skato
71842795Skato	case KDGKBMODE:		/* get keyboard mode */
71942795Skato		*(int *)arg = state->ks_mode;
72042795Skato		break;
721162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
722162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
723162711Sru	case _IO('K', 7):
724162711Sru		ival = IOCPARM_IVAL(arg);
725162711Sru		arg = (caddr_t)&ival;
726162711Sru		/* FALLTHROUGH */
727162711Sru#endif
72842795Skato	case KDSKBMODE:		/* set keyboard mode */
72942795Skato		switch (*(int *)arg) {
73042795Skato		case K_XLATE:
73142795Skato			if (state->ks_mode != K_XLATE) {
73242795Skato				/* make lock key state and LED state match */
73342795Skato				state->ks_state &= ~LOCK_MASK;
73442795Skato				state->ks_state |= KBD_LED_VAL(kbd);
73542795Skato			}
736102412Scharnier			/* FALLTHROUGH */
73742795Skato		case K_RAW:
73842795Skato		case K_CODE:
73942795Skato			if (state->ks_mode != *(int *)arg) {
74042795Skato				pckbd_clear_state(kbd);
74142795Skato				state->ks_mode = *(int *)arg;
74242795Skato			}
74342795Skato			break;
74442795Skato		default:
74542795Skato			splx(s);
74642795Skato			return EINVAL;
74742795Skato		}
74842795Skato		break;
74942795Skato
75042795Skato	case KDGETLED:		/* get keyboard LED */
75142795Skato		*(int *)arg = KBD_LED_VAL(kbd);
75242795Skato		break;
753162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
754162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
755162711Sru	case _IO('K', 66):
756162711Sru		ival = IOCPARM_IVAL(arg);
757162711Sru		arg = (caddr_t)&ival;
758162711Sru		/* FALLTHROUGH */
759162711Sru#endif
76042795Skato	case KDSETLED:		/* set keyboard LED */
76142795Skato		/* NOTE: lock key state in ks_state won't be changed */
76242795Skato		if (*(int *)arg & ~LOCK_MASK) {
76342795Skato			splx(s);
76442795Skato			return EINVAL;
76542795Skato		}
76642795Skato		i = *(int *)arg;
76742795Skato		/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
76842795Skato		if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
76942795Skato			if (i & ALKED)
77042795Skato				i |= CLKED;
77142795Skato			else
77242795Skato				i &= ~CLKED;
77342795Skato		}
77442795Skato		KBD_LED_VAL(kbd) = *(int *)arg;
77542795Skato		break;
77642795Skato
77742795Skato	case KDGKBSTATE:	/* get lock key state */
77842795Skato		*(int *)arg = state->ks_state & LOCK_MASK;
77942795Skato		break;
780162711Sru#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
781162711Sru    defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
782162711Sru	case _IO('K', 20):
783162711Sru		ival = IOCPARM_IVAL(arg);
784162711Sru		arg = (caddr_t)&ival;
785162711Sru		/* FALLTHROUGH */
786162711Sru#endif
78742795Skato	case KDSKBSTATE:	/* set lock key state */
78842795Skato		if (*(int *)arg & ~LOCK_MASK) {
78942795Skato			splx(s);
79042795Skato			return EINVAL;
79142795Skato		}
79242795Skato		state->ks_state &= ~LOCK_MASK;
79342795Skato		state->ks_state |= *(int *)arg;
79442795Skato		splx(s);
79542795Skato		/* set LEDs and quit */
79642795Skato		return pckbd_ioctl(kbd, KDSETLED, arg);
79742795Skato
79844635Skato	case KDSETRAD:		/* set keyboard repeat rate (old interface)*/
79942795Skato		break;
80044635Skato	case KDSETREPEAT:	/* set keyboard repeat rate (new interface) */
80144635Skato		break;
80242795Skato
80342795Skato	case PIO_KEYMAP:	/* set keyboard translation table */
804224126Sed	case OPIO_KEYMAP:	/* set keyboard translation table (compat) */
80542795Skato	case PIO_KEYMAPENT:	/* set keyboard translation table entry */
80642795Skato	case PIO_DEADKEYMAP:	/* set accent key translation table */
80742795Skato		state->ks_accents = 0;
808102412Scharnier		/* FALLTHROUGH */
80942795Skato	default:
81042795Skato		splx(s);
81142795Skato		return genkbd_commonioctl(kbd, cmd, arg);
81242795Skato	}
81342795Skato
81442795Skato	splx(s);
81542795Skato	return 0;
81642795Skato}
81742795Skato
81842795Skato/* lock the access to the keyboard */
81942795Skatostatic int
82042795Skatopckbd_lock(keyboard_t *kbd, int lock)
82142795Skato{
82242795Skato	return kbdc_lock(((pckbd_state_t *)kbd->kb_data)->kbdc, lock);
82342795Skato}
82442795Skato
82542795Skato/* clear the internal state of the keyboard */
82642795Skatostatic void
82742795Skatopckbd_clear_state(keyboard_t *kbd)
82842795Skato{
82942795Skato	pckbd_state_t *state;
83042795Skato
83142795Skato	state = (pckbd_state_t *)kbd->kb_data;
83242795Skato	state->ks_flags = 0;
83342795Skato	state->ks_state &= LOCK_MASK;	/* preserve locking key state */
83442795Skato	state->ks_accents = 0;
83542795Skato	state->ks_composed_char = 0;
83642795Skato}
83742795Skato
83842795Skato/* save the internal state */
83942795Skatostatic int
84042795Skatopckbd_get_state(keyboard_t *kbd, void *buf, size_t len)
84142795Skato{
84242795Skato	if (len == 0)
84342795Skato		return sizeof(pckbd_state_t);
84442795Skato	if (len < sizeof(pckbd_state_t))
84542795Skato		return -1;
84642795Skato	bcopy(kbd->kb_data, buf, sizeof(pckbd_state_t));
84742795Skato	return 0;
84842795Skato}
84942795Skato
85042795Skato/* set the internal state */
85142795Skatostatic int
85242795Skatopckbd_set_state(keyboard_t *kbd, void *buf, size_t len)
85342795Skato{
85442795Skato	if (len < sizeof(pckbd_state_t))
85542795Skato		return ENOMEM;
85642795Skato	if (((pckbd_state_t *)kbd->kb_data)->kbdc
85742795Skato		!= ((pckbd_state_t *)buf)->kbdc)
85842795Skato		return ENOMEM;
85942795Skato	bcopy(buf, kbd->kb_data, sizeof(pckbd_state_t));
86042795Skato	return 0;
86142795Skato}
86242795Skato
86344635Skato/* set polling mode */
86444635Skatostatic int
86544635Skatopckbd_poll(keyboard_t *kbd, int on)
86644635Skato{
86744635Skato	return 0;
86844635Skato}
86944635Skato
87042795Skato/* local functions */
87142795Skato
87242795Skatostatic int
87342795Skatoprobe_keyboard(KBDC kbdc, int flags)
87442795Skato{
87542795Skato	return 0;
87642795Skato}
87742795Skato
87842795Skatostatic int
87942795Skatoinit_keyboard(KBDC kbdc, int *type, int flags)
88042795Skato{
88142795Skato	*type = KB_OTHER;
88242795Skato	return 0;
88342795Skato}
88442795Skato
88542795Skato/* keyboard I/O routines */
88642795Skato
88742795Skato/* retry count */
88842795Skato#ifndef KBD_MAXRETRY
88942795Skato#define KBD_MAXRETRY	3
89042795Skato#endif
89142795Skato
89242795Skato/* timing parameters */
89342795Skato#ifndef KBD_RESETDELAY
89442795Skato#define KBD_RESETDELAY  200     /* wait 200msec after kbd/mouse reset */
89542795Skato#endif
89642795Skato#ifndef KBD_MAXWAIT
89742795Skato#define KBD_MAXWAIT	5 	/* wait 5 times at most after reset */
89842795Skato#endif
89942795Skato
90042795Skato/* I/O recovery time */
90142795Skato#define KBDC_DELAYTIME	37
90242795Skato#define KBDD_DELAYTIME	37
90342795Skato
90442795Skato/* I/O ports */
90542795Skato#define KBD_STATUS_PORT 	2	/* status port, read */
90642795Skato#define KBD_DATA_PORT		0	/* data port, read */
90742795Skato
90842795Skato/* status bits (KBD_STATUS_PORT) */
90942795Skato#define KBDS_BUFFER_FULL	0x0002
91042795Skato
91142795Skato/* macros */
91242795Skato
91342795Skato#define kbdcp(p)		((struct kbdc_softc *)(p))
91442795Skato
91542795Skato/* local variables */
91642795Skato
917102151Speterstatic struct kbdc_softc kbdc_softc[1] = { { 0 }, };
91842795Skato
91942795Skato/* associate a port number with a KBDC */
92042795Skato
92142795Skatostatic KBDC
92242795Skatokbdc_open(int port)
92342795Skato{
92442795Skato	if (port <= 0)
92542795Skato		port = IO_KBD;
92642795Skato
927102151Speter	/* PC-98 has only one keyboard I/F */
928102151Speter	kbdc_softc[0].port = port;
929102151Speter	kbdc_softc[0].lock = FALSE;
930102151Speter	return (KBDC)&kbdc_softc[0];
93142795Skato}
93242795Skato
93342795Skato/* set/reset polling lock */
93442795Skatostatic int
93542795Skatokbdc_lock(KBDC p, int lock)
93642795Skato{
93742795Skato    int prevlock;
93842795Skato
93942795Skato    prevlock = kbdcp(p)->lock;
94042795Skato    kbdcp(p)->lock = lock;
94142795Skato
94242795Skato    return (prevlock != lock);
94342795Skato}
94442795Skato
94542795Skato/* check if any data is waiting to be processed */
94642795Skatostatic int
94742795Skatokbdc_data_ready(KBDC p)
94842795Skato{
94942795Skato	return (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL);
95042795Skato}
95142795Skato
95242795Skato/* wait for data from the keyboard */
95342795Skatostatic int
95442795Skatowait_for_kbd_data(struct kbdc_softc *kbdc)
95542795Skato{
95642795Skato    /* CPU will stay inside the loop for 200msec at most */
95742795Skato    int retry = 10000;
95842795Skato    int port = kbdc->port;
95942795Skato
96042795Skato    while (!(inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL)) {
96142795Skato	DELAY(KBDD_DELAYTIME);
96242795Skato	DELAY(KBDC_DELAYTIME);
96342795Skato        if (--retry < 0)
96442795Skato    	    return 0;
96542795Skato    }
96642795Skato    DELAY(KBDD_DELAYTIME);
96742795Skato    return 1;
96842795Skato}
96942795Skato
97042795Skato/* read one byte from the keyboard */
97142795Skatostatic int
97242795Skatoread_kbd_data(KBDC p)
97342795Skato{
97442795Skato    if (!wait_for_kbd_data(kbdcp(p)))
97542795Skato        return -1;		/* timeout */
97642795Skato    DELAY(KBDC_DELAYTIME);
97742795Skato    return inb(kbdcp(p)->port + KBD_DATA_PORT);
97842795Skato}
97942795Skato
98042795Skato/* read one byte from the keyboard, but return immediately if
98142795Skato * no data is waiting
98242795Skato */
98342795Skatostatic int
98442795Skatoread_kbd_data_no_wait(KBDC p)
98542795Skato{
98642795Skato    if (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) {
98742795Skato        DELAY(KBDD_DELAYTIME);
98842795Skato        return inb(kbdcp(p)->port + KBD_DATA_PORT);
98942795Skato    }
99042795Skato    return -1;		/* no data */
99142795Skato}
992