/*	$NetBSD: kbd.c,v 1.24 1999/02/02 04:02:26 mark Exp $	*/

/*
 * Copyright (c) 1994-1997 Mark Brinicombe.
 * Copyright (c) 1994 Brini.
 * All rights reserved.
 *
 * This code is derived from software written for Brini by Mark Brinicombe
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Mark Brinicombe.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * RiscBSD kernel project
 *
 * kbd.c
 *
 * Keyboard driver functions
 *
 * Created      : 09/10/94
 */

#include "opt_ddb.h"
#include "opt_pmap_debug.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/vnode.h>
#include <sys/select.h>
#include <sys/fcntl.h>
#include <sys/signalvar.h>
#include <sys/time.h>
#include <sys/poll.h>

#include <machine/bus.h>
#include <machine/kbd.h>
#include <machine/conf.h>
#include <arm32/dev/kbdvar.h>
#include "vt.h"
#include "kbd.h"

/* Special keycodes */

#define KEYCODE_UP     0x100
#define KEYCODE_DOWN   0x101
#define KEYCODE_LEFT   0x102
#define KEYCODE_RIGHT  0x103
#define KEYCODE_PGUP   0x104
#define KEYCODE_PGDN   0x105
#define KEYCODE_INSERT 0x108
#define KEYCODE_DELETE 0x109
#define KEYCODE_HOME   0x10a
#define KEYCODE_END    0x10b

/* Key modifiers flags */

#define MODIFIER_CTRL   0x01
#define MODIFIER_SHIFT  0x02
#define MODIFIER_ALT    0x04
#define MODIFIER_MASK   0x07

#define MODIFIER_CAPS   0x20
#define MODIFIER_NUM    0x10
#define MODIFIER_SCROLL 0x08
#define MODIFIER_LOCK_MASK   0x38

#define MODIFIER_CAPSLOCK 0x40
#define MODIFIER_NORETURN 0x80

/* Keyboard variables */

struct kbd_softc *console_kbd = NULL;

#define RAWKBD_BSIZE 128

static int autorepeatkey = -1;
static struct kbd_autorepeat kbdautorepeat = { 5, 20 };
static int rawkbd_device = 0;
int modifiers = 0;
static int kbd_ack = 0;
static int kbd_resend = 0;

#ifdef PMAP_DEBUG
extern int pmap_debug_level;
#endif

#define KBDUNIT(u)	(minor(u) / 2)
#define KBDFLAG(u)	(minor(u) % 2) 

#define KBDFLAG_RAWUNIT	0
#define KBDFLAG_CONUNIT	1

extern key_struct keys[];
extern key_struct E0keys[];

/* Local function prototypes */

int	kbdreset __P((struct kbd_softc *sc));
void	kbd_flush_input __P((struct kbd_softc *sc));
int	kbdcmd __P((struct kbd_softc *sc, u_char cmd));
void	kbdsetleds __P((struct kbd_softc *sc, int leds));

int PollKeyboard __P((int));
int kbddecodekey __P((struct kbd_softc *, int));
int kbdintr __P((void *arg));

void autorepeatstart __P((void *));
void autorepeat __P((void *));

extern int physconkbd		__P((int key));
extern void console_switch	__P((u_int number));
/*extern int console_switchdown	__P((void));
extern int console_switchup	__P((void));*/
extern int console_unblank	__P((void));
extern int console_scrollback	__P((void));
extern int console_scrollforward	__P((void));
#ifdef PMAP_DEBUG
extern void pmap_debug		__P((int level));
#endif
extern void halt		__P((void));

extern struct cfdriver kbd_cd;

/* keyboard commands */

#define	KBC_RESET	0xFF	/* reset the keyboard */
#define	KBC_RESEND	0xFE	/* request the keyboard resend the last byte */
#define KBC_SET_TMB	0xFA	/* What is this one ? */
#define	KBC_SETDEFAULT	0xF6	/* resets keyboard to its power-on defaults */
#define	KBC_DISABLE	0xF5	/* as per KBC_SETDEFAULT, but also disable key scanning */
#define	KBC_ENABLE	0xF4	/* enable key scanning */
#define	KBC_TYPEMATIC	0xF3	/* set typematic rate and delay */
#define KBD_READID	0xF2	/* Read keyboard ID */
#define	KBC_SETSCANTBL	0xF0	/* set scancode translation table */
#define	KBC_SETLEDS	0xED	/* set mode indicators (i.e. LEDs) */
#define	KBC_ECHO	0xEE	/* request an echo from the keyboard */

#define KBD_SETSCAN_2	0x02
#define KBD_SETSCAN_3	0x03

/* keyboard responses */
#define	KBR_EXTENDED	0xE0	/* extended key sequence */
#define	KBR_RESEND	0xFE	/* needs resend of command */
#define	KBR_ACK		0xFA	/* received a valid command */
#define	KBR_OVERRUN	0x00	/* flooded */
#define	KBR_FAILURE	0xFD	/* diagnosic failure */
#define	KBR_BREAK	0xF0	/* break code prefix - sent on key release */
#define	KBR_RSTDONE	0xAA	/* reset complete */
#define	KBR_ECHO	0xEE	/* echo response */

#define	KBD_DATA	0
#define	KBD_CR		1
#define	KBD_STATUS	1

#define KBD_CR_ENABLE	0x08
#define KBD_CR_KDATAO	0x02
#define KBD_CR_KCLKO	0x01

#define KBD_ST_TXE	0x80
#define KBD_ST_TXB	0x40
#define KBD_ST_RXF	0x20
#define KBD_ST_RXB	0x10
#define KBD_ST_ENABLE	0x08
#define KBD_ST_RXPARITY	0x04
#define KBD_ST_KDATAI	0x02
#define KBD_ST_KCLKI	0x01

int
kbdopen(dev, flag, mode, p)
	dev_t dev;
	int flag;
	int mode;
	struct proc *p;
{
	struct kbd_softc *sc;
	int unit = KBDUNIT(dev);

	if (unit >= kbd_cd.cd_ndevs)
		return(ENXIO);

	sc = kbd_cd.cd_devs[unit];

	if (!sc) return(ENXIO);

	switch (KBDFLAG(dev)) {
	case KBDFLAG_RAWUNIT :
		if (sc->sc_state & RAWKBD_OPEN)
			return(EBUSY);
		sc->sc_state |= RAWKBD_OPEN;
		if (clalloc(&sc->sc_q, RAWKBD_BSIZE, 0) == -1)
			return(ENOMEM);
		sc->sc_proc = p;
		rawkbd_device = 1;
		break;
	case KBDFLAG_CONUNIT :
		if (sc->sc_state & KBD_OPEN)
			return(EBUSY);
		sc->sc_state |= KBD_OPEN;
		break;
	}

/* Kill any active autorepeat */

	untimeout(autorepeatstart, &autorepeatkey);
	untimeout(autorepeat, &autorepeatkey);

	return(0);
}


int
kbdclose(dev, flag, mode, p)
	dev_t dev;
	int flag;
	int mode;
	struct proc *p;
{
	int unit = KBDUNIT(dev);
	struct kbd_softc *sc = kbd_cd.cd_devs[unit];

	switch (KBDFLAG(dev)) {
	case KBDFLAG_RAWUNIT :
		if (!(sc->sc_state & RAWKBD_OPEN))
			return(EINVAL);
		sc->sc_state &= ~RAWKBD_OPEN;
		clfree(&sc->sc_q);
		sc->sc_proc = NULL;
		rawkbd_device = 0;
		break;
	case KBDFLAG_CONUNIT :
		if (!(sc->sc_state & KBD_OPEN))
			return(EINVAL);
		sc->sc_state &= ~KBD_OPEN;
		break;
	}

	return(0);
}


int
kbdread(dev, uio, flag)
	dev_t dev;
	struct uio *uio;
	int flag;
{
	struct kbd_softc *sc = kbd_cd.cd_devs[KBDUNIT(dev)];
	int s;
	int error = 0;
	size_t length;
	u_char buffer[128];

	if (KBDFLAG(dev) == KBDFLAG_CONUNIT)
		return(ENXIO);

	/* Block until keyboard activity occured. */

	s = spltty();
	while (sc->sc_q.c_cc == 0) {
		if (flag & IO_NDELAY) {
			splx(s);
			return EWOULDBLOCK;
		}
		sc->sc_state |= RAWKBD_ASLEEP;
		if ((error = tsleep((caddr_t)sc, PZERO | PCATCH, "kbdread", 0))) {
			sc->sc_state &= (~RAWKBD_ASLEEP);
			splx(s);
			return error;
		}
	}
	splx(s);

	/* Transfer as many chunks as possible. */

	while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
		length = min(sc->sc_q.c_cc, uio->uio_resid);
		if (length > sizeof(buffer))
			length = sizeof(buffer);

		/* Remove a small chunk from the input queue. */
		(void) q_to_b(&sc->sc_q, buffer, length);

		/* Copy the data to the user process. */
		if ((error = (uiomove(buffer, length, uio))))
			break;
	}
	return error;
}


int
kbdpoll(dev, events, p)
	dev_t dev;
	int events;
	struct proc *p;
{
	struct kbd_softc *sc = kbd_cd.cd_devs[KBDUNIT(dev)];
	int revents = 0;
	int s = spltty();

	if (KBDFLAG(dev) == KBDFLAG_CONUNIT)
		return(ENXIO);

	if (events & (POLLIN | POLLRDNORM)) {
		if (sc->sc_q.c_cc > 0)
			revents |= events & (POLLIN | POLLRDNORM);
		else
			selrecord(p, &sc->sc_rsel);
	}

	splx(s);
	return (revents);
}


int
kbdioctl(dev, cmd, data, flag, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flag;
	struct proc *p;
{
	struct kbd_softc *sc = kbd_cd.cd_devs[KBDUNIT(dev)];
	struct kbd_autorepeat *kbdar = (void *)data;
	int *leds = (int *)data;
	int s;

	switch (cmd) {
	case KBD_GETAUTOREPEAT:
/*		if (KBDFLAG(dev) == KBDFLAG_RAWUNIT)
			return(EINVAL);*/

		*kbdar = kbdautorepeat;
		break;
	case KBD_SETAUTOREPEAT:
/*		if (KBDFLAG(dev) == KBDFLAG_RAWUNIT)
			return(EINVAL);*/
		s = spltty();
		kbdautorepeat = *kbdar;
		if (kbdautorepeat.ka_rate < 1)
			kbdautorepeat.ka_rate = 1;
		if (kbdautorepeat.ka_rate > 50)
			kbdautorepeat.ka_rate = 50;
		if (kbdautorepeat.ka_delay > 50)
			kbdautorepeat.ka_delay = 50;
		if (kbdautorepeat.ka_delay < 1)
			kbdautorepeat.ka_delay = 1;
		(void)splx(s);
		break;
	case KBD_SETLEDS:
		kbdsetleds(sc, *leds);
		break;
	default:
		return(ENXIO);
	}
	return(0);
}


/* Low level keyboard driver functions */

/*
 * kbdcmd
 *
 * Send a command to the keyboard
 * Returns 0 if command succeeded or 1 if it failed.
 */

int
kbdcmd(sc, cmd)
	struct kbd_softc *sc;
	u_char cmd;
{
	u_char c;
	int i = 0;
	int retry;

	for (retry = 7; retry >= 0; --retry) {

		/* Wait for empty kbd transmit register */
		 
		for (i = 1000; i; i--) {
			if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_STATUS) & KBD_ST_TXE)
				break;
			delay(200);
		}
		if (i == 0)
			printf("%s: transmit not ready\n", sc->sc_device.dv_xname);

		bus_space_write_1(sc->sc_iot, sc->sc_ioh, KBD_DATA, cmd);
		delay(200);
	
		/* Wait for full kbd receive register */

		for (i = 0; i < 1000; i++) {
			c = bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_STATUS);
			if ((c & KBD_ST_RXF) == KBD_ST_RXF)
				break;
			delay(100);
		}

		delay(100);

		/* Get byte from kbd receive register */

		c = bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_DATA);
		if ((c == KBR_ACK) || (c == KBR_ECHO))
			return(0);

		/* Failed if we have more reties to go flush kbd */

		if (retry)
			kbd_flush_input(sc);
	}
	printf("%s: command failed, cmd = %02x, status = %02x\n", sc->sc_device.dv_xname, cmd, c);
	return(1);
}


/*
 * kbd_flush_input()
 *
 * Flushes the keyboard input register
 */

void
kbd_flush_input(sc)
	struct kbd_softc *sc;
{
	int i;

	/* Loop round reading bytes while the receive buffer is not empty */

	for (i = 0; i < 20; i++) {
		if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_STATUS) & KBD_ST_RXF) == 0)
			break;
		delay(100);
		(void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_DATA);
	}
}


/*
 * kbdreset()
 *
 * Resets the keyboard.
 * Returns 0 if successful.
 * Returns 1 if keyboard could not be enabled.
 * Returns 2 if keyboard could not be reset.
 */

int
kbdreset(sc)
	struct kbd_softc *sc;
{
	int i;
	u_char c;

	kbd_flush_input(sc);

	/* Disable, wait then enable the keyboard interface */

	bus_space_write_1(sc->sc_iot, sc->sc_ioh, KBD_CR, 0x00);
	delay(100);
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, KBD_CR, KBD_CR_ENABLE);

	/* Wait for kdata and kclk to go high */

	for (i = 1000; i; i--) {
		if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_STATUS)
		    & (KBD_ST_KDATAI | KBD_ST_KCLKI))
		    == (KBD_ST_KDATAI | KBD_ST_KCLKI))
			break;
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, KBD_CR,
		    (KBD_CR_KDATAO | KBD_CR_KCLKO));
		delay(200);
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, KBD_CR, KBD_CR_ENABLE);
	}
	if (i == 0 || (bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_STATUS)
	    & KBD_ST_ENABLE) == 0)
		return(1);

	kbd_flush_input(sc);
	kbdcmd(sc, KBC_DISABLE);

retry:
	kbdcmd(sc, KBC_RESET);
	delay(500000);
	for (i = 100; i; i--) {
		c = bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_DATA);
		if (c == KBR_RSTDONE)
			break;
		delay(100000);
		if (c == KBR_RESEND) {
			printf(" [retry]");
			goto retry;
		}
	}
	if (i == 0)
		return(2);

	kbdcmd(sc, KBC_SETSCANTBL);
	kbdcmd(sc, KBD_SETSCAN_2);
	kbdcmd(sc, KBC_ENABLE);

	modifiers = MODIFIER_NUM;
	kbdsetleds(sc, (modifiers >> 3) & 7);
	return(0);
}


void
kbdsetleds(sc, leds)
	struct kbd_softc *sc;
	int leds;
{
	kbdcmd(sc, KBC_SETLEDS);
	kbdcmd(sc, leds);
}

#if 0
void
kbdsetstate(sc, state)
	struct kbd_softc *sc;
	int state;
{
	modifiers = state & MODIFIER_LOCK_MASK;
	kbdsetleds(sc, (modifiers >> 3) & 7);
}


int
kdbgetstate(sc)
	struct kbd_softc *sc;
{
	return(modifiers);
}
#endif
  
int
getkey_polled()
{
	int code;
	int key;
	int up;
	key_struct *ks;
	int s;

	if (console_kbd == NULL) {
		/*
		 * Cannot panic here as that will either call the debugger or
		 * cpu_reboot() which will try and read a key.
		 */
		printf("console_kbd = 0\n");
		halt();
	}
	
	s = splhigh();

	key = 0;

	do {
		while ((bus_space_read_1(console_kbd->sc_iot, console_kbd->sc_ioh, KBD_STATUS) & KBD_ST_RXF) == 0) ;
		delay(10);

		/* Read the IOMD keyboard register and process the key */

		code = PollKeyboard(bus_space_read_1(console_kbd->sc_iot, console_kbd->sc_ioh, KBD_DATA));

		if (code != 0) {
			up = (code & 0x100);
			key = code & 0xff;

/*			printf("code=%04x mod=%04x\n", code, modifiers);*/

			/* By default we use the main keycode lookup table */

			ks = keys;

			/* If we have an E0 or E1 sqeuence we use the extended table */

			if (code > 0x1ff)
				ks = E0keys;

			/* Is the key a temporary modifier ? */

			if (ks[key].flags & MODIFIER_MASK) {
				if (up)
					modifiers &= ~ks[key].flags;
				else
					modifiers |= ks[key].flags;
				key = 0;
				continue;
			}

/* Is the key a locking modifier ? */

			if (ks[key].flags & MODIFIER_LOCK_MASK) {
				if (!up) {
					modifiers ^= ks[key].flags;
					kbdsetleds(console_kbd, (modifiers >> 3) & 7);
				}
				key = 0;
				continue;
			}

/* Lookup the correct key code */

			if (modifiers & 0x01)
				key = ks[key].ctrl_code;
			else if (modifiers & 0x02)
				key = ks[key].shift_code;
			else if (modifiers & 0x04)
				key = ks[key].alt_code;
			else
				key = ks[key].base_code;

			if (modifiers & MODIFIER_CAPS) {
				if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'))
					key ^= 0x20;
			}

			if (up)
				key = 0;
			if (!up && key >= 0x200) {

#if (NVT > 0)
				if ((key & ~0x0f) == 0x480)
					console_switch((key & 0x0f) - 1);
				else
#endif
				switch (key) {
#if (NVT > 0)
				case 0x201:
					console_scrollforward();
					break;
				case 0x200:
					console_scrollback();
					break;
#endif
				default:
					break;
				}
				key = 0;
			}
		}
	} while (key == 0);

	if (key == '\r')
		key = '\n';

	splx(s);
	return(key);
}


/* Keyboard IRQ handler */

int
kbdintr(arg)
	void *arg;
{
	struct kbd_softc *sc = arg;
	int key;

/* Read the IOMD keyboard register and process the key */

	key = PollKeyboard(bus_space_read_1(sc->sc_iot, sc->sc_ioh, KBD_DATA));

/* If we have a raw keycode convert it to an ASCII code */

	if (key != 0)
		kbddecodekey(sc, key);

	return(1);	/* Claim interrupt */
}


/* Flags used to decode the raw keys */

#define FLAG_KEYUP	0x01
#define FLAG_E0		0x02
#define FLAG_E1		0x04

static int flags = 0;

/*
 * This function is now misnamed.
 * It processes the raw key codes from the keyboard and generates
 * a unique code that can be decoded with the key translation array.
 */

int
PollKeyboard(code)
	int code;
{
/*	printf("%02x.", code);*/

/*
 * Set the keyup flag if this is the release code.
 */

	if (code == 0xf0) {
		flags |= FLAG_KEYUP;
		return(0);
	}

/* If it is a special code ignore it */

	if (code == 0xff || code == 0x00) {
		flags = 0;
		return(0);
	}

/* If it is a resend code note it down */

	if (code == KBR_RESEND) {
		printf("kbd:resend\n");
		kbd_resend = 1;
		return(0);
	}

/* If it is an ack code note it down */

	if (code == KBR_ACK) {
/*		printf("kbd:ack\n");*/
		kbd_ack = 1;
		return(0);
	}

/* Flag the start of an E0 sequence. */

	if (code == 0xe0) {
		flags |= FLAG_E0;
		return(0);
	}

/* Flag the start of an E1 sequence. */

	if (code == 0xe1) {
		flags |= FLAG_E1;
		return(0);
	}

/* Ignore any other invalid codes */

	if (code > 0x8f) {
		flags = 0;
		return(0);
	}

/*	printf("%02x:%02x.", code, flags);*/

/* Mark the code appropriately if it is part of an E0 sequence */

	if (flags & FLAG_E0) {
		flags &= ~FLAG_E0;
		if (code == 0x12) {
			flags &= ~FLAG_KEYUP;
			return(0);
		}
		code |= 0x200;
	}

/* Mark the key if it is the upcode */

	if (flags & FLAG_KEYUP) {
		flags &= ~FLAG_KEYUP;
		code |= 0x100;
	}

/* Mark the code appropriately if it is part of an E1 sequence */

	if (flags & FLAG_E1) {
		if ((code & 0xff) == 0x14) {
			return(0);
		}
		flags &= ~FLAG_E1;
		code |= 0x400;
		flags &= ~FLAG_KEYUP;
	}

	return(code);
}


/*
 * This routine decodes the unique keycode and generates an ASCII code
 * if necessary.
 */

int
kbddecodekey(sc, code)
	struct kbd_softc *sc;
	int code;
{
	key_struct *ks;
	int up;
	int key;

	console_unblank();

/* Do we have the raw kbd device open ... */

	if (rawkbd_device == 1 && code != 0) {
		struct kbd_data buffer;
		int s;
  
  /* Add this event to the queue. */
 
 		buffer.keycode = code;
 		microtime(&buffer.event_time);
		s=spltty();
 		(void) b_to_q((char *)&buffer, sizeof(buffer), &sc->sc_q);
 		splx(s);
		selwakeup(&sc->sc_rsel);

		if (sc->sc_state & RAWKBD_ASLEEP) {
			sc->sc_state &= ~RAWKBD_ASLEEP;
			wakeup((caddr_t)sc);
		}

		psignal(sc->sc_proc, SIGIO);
		return(1);
	}

	up = (code & 0x100);
	key = code & 0xff;

/* By default we use the main keycode lookup table */

	ks = keys;

/* If we have an E0 or E1 sqeuence we use the extended table */

	if (code > 0x1ff)
		ks = E0keys;

/* Is the key a temporary modifier ? */

	if (ks[key].flags & MODIFIER_MASK) {
		if (up)
			modifiers &= ~ks[key].flags;
		else
			modifiers |= ks[key].flags;
		return(0);
	}

/* Is the key a locking modifier ? */

	if (ks[key].flags & MODIFIER_LOCK_MASK) {
		if (!up) {
			modifiers ^= ks[key].flags;
			kbdsetleds(sc, (modifiers >> 3) & 7);
		}
		return(0);
	}

/* Lookup the correct key code */

	if (modifiers & 0x01)
		key = ks[key].ctrl_code;
	else if (modifiers & 0x02)
		key = ks[key].shift_code;
	else if (modifiers & 0x04)
		key = ks[key].alt_code;
	else
		key = ks[key].base_code;

	if (modifiers & MODIFIER_CAPS) {
		if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'))
			key ^= 0x20;
	}

/* If no valid code the key is not yet mapped so report error */

#ifdef DEBUG_TERM
/*	if (key == 0) {
		char err[80];

		sprintf(err, "\n\rUnknown keycode %04x\n\r", code);
		dprintf(err);
 
	}*/
#endif

/* If we have an ASCII code insert it into the keyboard buffer */

	if (!up && key != 0) {
		if (key >= 0x200) {

			untimeout(autorepeatstart, &autorepeatkey);
			untimeout(autorepeat, &autorepeatkey);
			autorepeatkey = -1;
#if (NVT > 0)
			if ((key & ~0x0f) == 0x480)
				console_switch((key & 0x0f) - 1);
			else
#endif
				switch (key) {
#ifdef PMAP_DEBUG
				case 0x22b:
					pmap_debug(pmap_debug_level + 1);
					break;
				case 0x22d:
					pmap_debug(pmap_debug_level - 1);
					break;
#endif
#if (NVT > 0)
				case 0x201:
					console_scrollforward();
					break;
				case 0x200:
					console_scrollback();
					break;
/*
				case 0x202:
					console_switchdown();
					break;
				case 0x203:
					console_switchup();
					break;
*/
#endif
				case 0x204:
					--kbdautorepeat.ka_rate;
					if (kbdautorepeat.ka_rate < 1)
						kbdautorepeat.ka_rate = 1;
					break;
				case 0x205:
					++kbdautorepeat.ka_rate;
					if (kbdautorepeat.ka_rate > 50)
						kbdautorepeat.ka_rate = 50;
					break;
				case 0x20a:
					++kbdautorepeat.ka_delay;
					if (kbdautorepeat.ka_delay > 50)
						kbdautorepeat.ka_delay = 50;
					break;
				case 0x20b:
					--kbdautorepeat.ka_delay;
					if (kbdautorepeat.ka_delay < 1)
						kbdautorepeat.ka_delay = 1;
					break;
#ifdef DDB
				case 0x208:
					Debugger();
					break;
#endif
				case 0x21b:
					printf("Kernel interruption\n");
					cpu_reboot(RB_HALT, NULL);
					break;
				case 0x209:
					printf("Kernel interruption - nosync\n");
					cpu_reboot(RB_NOSYNC | RB_HALT, NULL);
					break;
	
				default:
					printf("Special key %04x\n", key);
					break;
				}
		} else {
			/*
			 * Check rawkbd_device first, in case we stick in the
			 * physconkbd().
			 */
			if (rawkbd_device == 0 && physconkbd(key) == 0) {
				if (autorepeatkey != key) {
					untimeout(autorepeatstart, &autorepeatkey);
					untimeout(autorepeat, &autorepeatkey);
					autorepeatkey = key;
					timeout(autorepeatstart, &autorepeatkey, hz/kbdautorepeat.ka_delay);
				}
			}

			return(1);
		}
	} else {
		untimeout(autorepeatstart, &autorepeatkey);
		untimeout(autorepeat, &autorepeatkey);
		autorepeatkey = -1;
	}
	return(0);
}


void
autorepeatstart(key)
	void	*key;
{
	physconkbd(*((int *)key));
	timeout(autorepeat, key, hz/kbdautorepeat.ka_rate);
}


void
autorepeat(key)
	void	*key;
{
	physconkbd(*((int *)key));
	timeout(autorepeat, key, hz/kbdautorepeat.ka_rate);
}

/* End of kbd.c */
