/*	$NetBSD: pccons.c,v 1.63 2022/05/24 20:50:18 andvar Exp $	*/
/*	$OpenBSD: pccons.c,v 1.22 1999/01/30 22:39:37 imp Exp $	*/
/*	NetBSD: pccons.c,v 1.89 1995/05/04 19:35:20 cgd Exp	*/

/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz and Don Ahn.
 *
 * 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. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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.
 *
 *	@(#)pccons.c	5.11 (Berkeley) 5/21/91
 */

/*-
 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz and Don Ahn.
 *
 * Copyright (c) 1994 Charles M. Hannum.
 * Copyright (c) 1992, 1993 Erik Forsberg.
 *
 * 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 the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS 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.
 *
 *	@(#)pccons.c	5.11 (Berkeley) 5/21/91
 */

/*
 * code to work keyboard & display for PC-style console
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pccons.c,v 1.63 2022/05/24 20:50:18 andvar Exp $");

#include "opt_ddb.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/callout.h>
#include <sys/poll.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/kcore.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/kauth.h>

#include <sys/bus.h>

#include <dev/ic/pcdisplay.h>
#include <machine/pccons.h>
#include <machine/kbdreg.h>

#include <dev/cons.h>
#include <dev/isa/isavar.h>

#include <arc/arc/arcbios.h>
#include <arc/dev/pcconsvar.h>

#include "ioconf.h"

#define	XFREE86_BUG_COMPAT

#ifndef BEEP_FREQ
#define BEEP_FREQ 1600
#endif
#ifndef BEEP_TIME
#define BEEP_TIME (hz/5)
#endif

#define PCBURST 128

static u_short *Crtat;			/* pointer to backing store */
static u_short *crtat;			/* pointer to current char */
static u_char async, kernel, polling;	/* Really, you don't want to know. */
static u_char lock_state = 0x00,	/* all off */
	      old_lock_state = 0xff,
	      typematic_rate = 0xff,	/* don't update until set by user */
	      old_typematic_rate = 0xff;
static u_short cursor_shape = 0xffff,	/* don't update until set by user */
	       old_cursor_shape = 0xffff;
static pccons_keymap_t scan_codes[KB_NUM_KEYS];/* keyboard translation table */
int pc_xmode = 0;

/*
 *  Keyboard output queue.
 */
int	kb_oq_put = 0;
int	kb_oq_get = 0;
u_char	kb_oq[8];

#define	PCUNIT(x)	(minor(x))

static struct video_state {
	int 	cx, cy;		/* escape parameters */
	int 	row, col;	/* current cursor position */
	int 	nrow, ncol, nchr;	/* current screen geometry */
	int	offset;		/* Saved cursor pos */
	u_char	state;		/* parser state */
#define	VSS_ESCAPE	1
#define	VSS_EBRACE	2
#define	VSS_EPARAM	3
	char	so;		/* in standout mode? */
	char	color;		/* color or mono display */
	char	at;		/* normal attributes */
	char	so_at;		/* standout attributes */
} vs;

static callout_t async_update_ch;

void pc_xmode_on(void);
void pc_xmode_off(void);
static u_char kbc_get8042cmd(void);
int kbd_cmd(u_char, u_char);
static inline int kbd_wait_output(void);
static inline int kbd_wait_input(void);
void kbd_flush_input(void);
void set_cursor_shape(void);
void get_cursor_shape(void);
void async_update(void);
void do_async_update(u_char);

void pccnputc(dev_t, int c);
int pccngetc(dev_t);
void pccnpollc(dev_t, int);

dev_type_open(pcopen);
dev_type_close(pcclose);
dev_type_read(pcread);
dev_type_write(pcwrite);
dev_type_ioctl(pcioctl);
dev_type_tty(pctty);
dev_type_poll(pcpoll);
dev_type_mmap(pcmmap);

const struct cdevsw pc_cdevsw = {
	.d_open = pcopen,
	.d_close = pcclose,
	.d_read = pcread,
	.d_write = pcwrite,
	.d_ioctl = pcioctl,
	.d_stop = nostop,
	.d_tty = pctty,
	.d_poll = pcpoll,
	.d_mmap = pcmmap,
	.d_kqfilter = ttykqfilter,
	.d_discard = nodiscard,
	.d_flag = D_TTY
};

#define	CHR		2

char *sget(void);
void sput(const u_char *, int);

void	pcstart(struct tty *);
int	pcparam(struct tty *, struct termios *);
static inline void wcopy(void *, void *, u_int);
void	pc_context_init(bus_space_tag_t, bus_space_tag_t, bus_space_tag_t,
	    struct pccons_config *);

extern void fillw(int, uint16_t *, int);

#define	KBD_DELAY \
		DELAY(10);

#define crtc_read_1(reg) \
	bus_space_read_1(pccons_console_context.pc_crt_iot, \
	    pccons_console_context.pc_6845_ioh, reg)
#define crtc_write_1(reg, data) \
	bus_space_write_1(pccons_console_context.pc_crt_iot, \
	    pccons_console_context.pc_6845_ioh, reg, data)

struct pccons_context pccons_console_context;

void
kbd_context_init(bus_space_tag_t kbd_iot, struct pccons_config *config)
{
	struct pccons_kbd_context *pkc = &pccons_console_context.pc_pkc;

	if (pkc->pkc_initialized)
		return;
	pkc->pkc_initialized = 1;

	pkc->pkc_iot = kbd_iot;

	bus_space_map(kbd_iot, config->pc_kbd_cmdp, 1, 0,
	    &pkc->pkc_cmd_ioh);
	bus_space_map(kbd_iot, config->pc_kbd_datap, 1, 0,
	    &pkc->pkc_data_ioh);
}

void
pc_context_init(bus_space_tag_t crt_iot, bus_space_tag_t crt_memt,
    bus_space_tag_t kbd_iot, struct pccons_config *config)
{
	struct pccons_context *pc = &pccons_console_context;

	if (pc->pc_initialized)
		return;
	pc->pc_initialized = 1;

	kbd_context_init(kbd_iot, config);

	pc->pc_crt_iot = crt_iot;
	pc->pc_crt_memt = crt_memt;

	bus_space_map(crt_iot, config->pc_mono_iobase, 2, 0,
	    &pc->pc_mono_ioh);
	bus_space_map(crt_memt, config->pc_mono_memaddr, 0x20000, 0,
	    &pc->pc_mono_memh);
	bus_space_map(crt_iot, config->pc_cga_iobase, 2, 0,
	    &pc->pc_cga_ioh);
	bus_space_map(crt_memt, config->pc_cga_memaddr, 0x20000, 0,
	    &pc->pc_cga_memh);

	/*
	 * pc->pc_6845_ioh and pc->pc_crt_memh will be initialized later,
	 * when `Crtat' is initialized.
	 */

	pc->pc_config = config;

	(*config->pc_init)();
}

/*
 * bcopy variant that only moves word-aligned 16-bit entities,
 * for stupid VGA cards.  cnt is required to be an even value.
 */
static inline void
wcopy(void *src, void *tgt, u_int cnt)
{
	uint16_t *from = src;
	uint16_t *to = tgt;

	cnt >>= 1;
	if (to < from || to >= from + cnt)
		while (cnt--)
			*to++ = *from++;
	else {
		to += cnt;
		from += cnt;
		while (cnt--)
			*--to = *--from;
	}
}

static inline int
kbd_wait_output(void)
{
	u_int i;

	for (i = 100000; i; i--)
		if ((kbd_cmd_read_1() & KBS_IBF) == 0) {
			KBD_DELAY;
			return 1;
		}
	return 0;
}

static inline int
kbd_wait_input(void)
{
	u_int i;

	for (i = 100000; i; i--)
		if ((kbd_cmd_read_1() & KBS_DIB) != 0) {
			KBD_DELAY;
			return 1;
		}
	return 0;
}

void
kbd_flush_input(void)
{
	uint8_t c;

	while ((c = kbd_cmd_read_1()) & 0x03)
		if ((c & KBS_DIB) == KBS_DIB) {
			/* XXX - delay is needed to prevent some keyboards from
			   wedging when the system boots */
			delay(6);
			(void)kbd_data_read_1();
		}
}

#if 1
/*
 * Get the current command byte.
 */
static u_char
kbc_get8042cmd(void)
{

	if (!kbd_wait_output())
		return -1;
	kbd_cmd_write_1(K_RDCMDBYTE);
	if (!kbd_wait_input())
		return -1;
	return kbd_data_read_1();
}
#endif

/*
 * Pass command byte to keyboard controller (8042).
 */
int
kbc_put8042cmd(uint8_t val)
{

	if (!kbd_wait_output())
		return 0;
	kbd_cmd_write_1(K_LDCMDBYTE);
	if (!kbd_wait_output())
		return 0;
	kbd_data_write_1(val);
	return 1;
}

/*
 * Pass command to keyboard itself
 */
int
kbd_cmd(uint8_t val, uint8_t polled)
{
	u_int retries = 3;
	u_int i;

	if (!polled) {
		i = spltty();
		if (kb_oq_get == kb_oq_put) {
			kbd_data_write_1(val);
		}
		kb_oq[kb_oq_put] = val;
		kb_oq_put = (kb_oq_put + 1) & 7;
		splx(i);
		return 1;
	}

	do {
		if (!kbd_wait_output())
			return 0;
		kbd_data_write_1(val);
		for (i = 100000; i; i--) {
			if (kbd_cmd_read_1() & KBS_DIB) {
				uint8_t c;

				KBD_DELAY;
				c = kbd_data_read_1();
				if (c == KBR_ACK || c == KBR_ECHO) {
					return 1;
				}
				if (c == KBR_RESEND) {
					break;
				}
#ifdef DIAGNOSTIC
				printf("kbd_cmd: input char %x lost\n", c);
#endif
			}
		}
	} while (--retries);
	return 0;
}

void
set_cursor_shape(void)
{

	crtc_write_1(0, 10);
	crtc_write_1(1, cursor_shape >> 8);
	crtc_write_1(0, 11);
	crtc_write_1(1, cursor_shape);
	old_cursor_shape = cursor_shape;
}

void
get_cursor_shape(void)
{

	crtc_write_1(0, 10);
	cursor_shape = crtc_read_1(1) << 8;
	crtc_write_1(0, 11);
	cursor_shape |= crtc_read_1(1);

	/*
	 * real 6845's, as found on, MDA, Hercules or CGA cards, do
	 * not support reading the cursor shape registers. the 6845
	 * tri-states its data bus. This is _normally_ read by the
	 * CPU as either 0x00 or 0xff.. in which case we just use
	 * a line cursor.
	 */
	if (cursor_shape == 0x0000 || cursor_shape == 0xffff)
		cursor_shape = 0x0b10;
	else
		cursor_shape &= 0x1f1f;
}

void
do_async_update(uint8_t poll)
{
	int pos;
	static int old_pos = -1;

	async = 0;

	if (lock_state != old_lock_state) {
		old_lock_state = lock_state;
		if (!kbd_cmd(KBC_MODEIND, poll) ||
		    !kbd_cmd(lock_state, poll)) {
			printf("pc: timeout updating leds\n");
			(void) kbd_cmd(KBC_ENABLE, poll);
		}
	}
	if (typematic_rate != old_typematic_rate) {
		old_typematic_rate = typematic_rate;
		if (!kbd_cmd(KBC_TYPEMATIC, poll) ||
		    !kbd_cmd(typematic_rate, poll)) {
			printf("pc: timeout updating typematic rate\n");
			(void) kbd_cmd(KBC_ENABLE, poll);
		}
	}

	if (pc_xmode > 0)
		return;

	pos = crtat - Crtat;
	if (pos != old_pos) {
		crtc_write_1(0, 14);
		crtc_write_1(1, pos >> 8);
		crtc_write_1(0, 15);
		crtc_write_1(1, pos);
		old_pos = pos;
	}
	if (cursor_shape != old_cursor_shape)
		set_cursor_shape();
}

void
async_update(void)
{

	if (kernel || polling) {
		if (async)
			callout_stop(&async_update_ch);
		do_async_update(1);
	} else {
		if (async)
			return;
		async = 1;
		callout_reset(&async_update_ch, 1,
		    (void(*)(void *))do_async_update, NULL);
	}
}

/*
 * these are both bad jokes
 */
int
pccons_common_match(bus_space_tag_t crt_iot, bus_space_tag_t crt_memt,
    bus_space_tag_t kbd_iot, struct pccons_config *config)
{
	int i;

	pc_context_init(crt_iot, crt_memt, kbd_iot, config);

	/* Enable interrupts and keyboard, etc. */
	if (!kbc_put8042cmd(CMDBYTE)) {
		printf("pcprobe: command error\n");
		return 0;
	}

#if 1
	/* Flush any garbage. */
	kbd_flush_input();
	/* Reset the keyboard. */
	if (!kbd_cmd(KBC_RESET, 1)) {
		printf("pcprobe: reset error %d\n", 1);
		goto lose;
	}
	for (i = 600000; i; i--)
		if ((kbd_cmd_read_1() & KBS_DIB) != 0) {
			KBD_DELAY;
			break;
		}
	if (i == 0 || kbd_data_read_1() != KBR_RSTDONE) {
		printf("pcprobe: reset error %d\n", 2);
		goto lose;
	}
	/*
	 * Some keyboards seem to leave a second ack byte after the reset.
	 * This is kind of stupid, but we account for them anyway by just
	 * flushing the buffer.
	 */
	kbd_flush_input();
	/* Just to be sure. */
	if (!kbd_cmd(KBC_ENABLE, 1)) {
		printf("pcprobe: reset error %d\n", 3);
		goto lose;
	}

	/*
	 * Some keyboard/8042 combinations do not seem to work if the keyboard
	 * is set to table 1; in fact, it would appear that some keyboards just
	 * ignore the command altogether.  So by default, we use the AT scan
	 * codes and have the 8042 translate them.  Unfortunately, this is
	 * known to not work on some PS/2 machines.  We try desperately to deal
	 * with this by checking the (lack of a) translate bit in the 8042 and
	 * attempting to set the keyboard to XT mode.  If this all fails, well,
	 * tough luck.
	 *
	 * XXX It would perhaps be a better choice to just use AT scan codes
	 * and not bother with this.
	 */
	if (kbc_get8042cmd() & KC8_TRANS) {
		/* The 8042 is translating for us; use AT codes. */
		if (!kbd_cmd(KBC_SETTABLE, 1) || !kbd_cmd(2, 1)) {
			printf("pcprobe: reset error %d\n", 4);
			goto lose;
		}
	} else {
		/* Stupid 8042; set keyboard to XT codes. */
		if (!kbd_cmd(KBC_SETTABLE, 1) || !kbd_cmd(1, 1)) {
			printf("pcprobe: reset error %d\n", 5);
			goto lose;
		}
	}

lose:
	/*
	 * Technically, we should probably fail the probe.  But we'll be nice
	 * and allow keyboard-less machines to boot with the console.
	 */
#endif

	return 1;
}

void pccons_common_attach(struct pc_softc *sc, bus_space_tag_t crt_iot,
    bus_space_tag_t crt_memt, bus_space_tag_t kbd_iot,
    struct pccons_config *config)
{

	printf(": %s\n", vs.color ? "color" : "mono");
	callout_init(&async_update_ch, 0);
	do_async_update(1);
}

int
pcopen(dev_t dev, int flag, int mode, struct lwp *l)
{
	struct pc_softc *sc;
	struct tty *tp;

	sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	if (sc == NULL)
		return ENXIO;

	if (!sc->sc_tty) {
		tp = sc->sc_tty = tty_alloc();
	}
	else {
		tp = sc->sc_tty;
	}

	tp->t_oproc = pcstart;
	tp->t_param = pcparam;
	tp->t_dev = dev;

	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
		return (EBUSY);

	if ((tp->t_state & TS_ISOPEN) == 0) {
		ttychars(tp);
		tp->t_iflag = TTYDEF_IFLAG;
		tp->t_oflag = TTYDEF_OFLAG;
		tp->t_cflag = TTYDEF_CFLAG;
		tp->t_lflag = TTYDEF_LFLAG;
		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
		pcparam(tp, &tp->t_termios);
		ttsetwater(tp);
	}

	tp->t_state |= TS_CARR_ON;

	return (*tp->t_linesw->l_open)(dev, tp);
}

int
pcclose(dev_t dev, int flag, int mode, struct lwp *l)
{
	struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	struct tty *tp = sc->sc_tty;

	(*tp->t_linesw->l_close)(tp, flag);
	ttyclose(tp);
#ifdef notyet /* XXX */
	tty_free(tp);
#endif
	return 0;
}

int
pcread(dev_t dev, struct uio *uio, int flag)
{
	struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	struct tty *tp = sc->sc_tty;

	return (*tp->t_linesw->l_read)(tp, uio, flag);
}

int
pcwrite(dev_t dev, struct uio *uio, int flag)
{
	struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	struct tty *tp = sc->sc_tty;

	return (*tp->t_linesw->l_write)(tp, uio, flag);
}

int
pcpoll(dev_t dev, int events, struct lwp *l)
{
	struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	struct tty *tp = sc->sc_tty;

	return (*tp->t_linesw->l_poll)(tp, events, l);
}

struct tty *
pctty(dev_t dev)
{
	struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	struct tty *tp = sc->sc_tty;

	return tp;
}

/*
 * Got a console receive interrupt -
 * the console processor wants to give us a character.
 * Catch the character, and see who it goes to.
 */
int
pcintr(void *arg)
{
	struct pc_softc *sc = arg;
	struct tty *tp = sc->sc_tty;
	uint8_t *cp;

	if ((kbd_cmd_read_1() & KBS_DIB) == 0)
		return 0;
	if (polling)
		return 1;
	do {
		cp = sget();
		if (!tp || (tp->t_state & TS_ISOPEN) == 0)
			return 1;
		if (cp)
			do
				(*tp->t_linesw->l_rint)(*cp++, tp);
			while (*cp);
	} while (kbd_cmd_read_1() & KBS_DIB);
	return 1;
}

int
pcioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
	struct pc_softc *sc = device_lookup_private(&pc_cd, PCUNIT(dev));
	struct tty *tp = sc->sc_tty;
	int error;

	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
	if (error != EPASSTHROUGH)
		return error;
	error = ttioctl(tp, cmd, data, flag, l);
	if (error != EPASSTHROUGH)
		return error;

	switch (cmd) {
	case CONSOLE_X_MODE_ON:
		pc_xmode_on();
		return 0;
	case CONSOLE_X_MODE_OFF:
		pc_xmode_off();
		return 0;
	case CONSOLE_X_BELL:
		/*
		 * If set, data is a pointer to a length 2 array of
		 * integers.  data[0] is the pitch in Hz and data[1]
		 * is the duration in msec.
		 */
		if (data)
			sysbeep(((int*)data)[0],
				(((int*)data)[1] * hz) / 1000);
		else
			sysbeep(BEEP_FREQ, BEEP_TIME);
		return 0;
	case CONSOLE_SET_TYPEMATIC_RATE: {
 		u_char	rate;

 		if (!data)
			return EINVAL;
		rate = *((u_char *)data);
		/*
		 * Check that it isn't too big (which would cause it to be
		 * confused with a command).
		 */
		if (rate & 0x80)
			return EINVAL;
		typematic_rate = rate;
		async_update();
		return 0;
 	}
	case CONSOLE_SET_KEYMAP: {
		pccons_keymap_t *map = (pccons_keymap_t *) data;
		int i;

		if (!data)
			return EINVAL;
		for (i = 0; i < KB_NUM_KEYS; i++)
			if (map[i].unshift[KB_CODE_SIZE-1] ||
			    map[i].shift[KB_CODE_SIZE-1] ||
			    map[i].ctl[KB_CODE_SIZE-1] ||
			    map[i].altgr[KB_CODE_SIZE-1] ||
			    map[i].shift_altgr[KB_CODE_SIZE-1])
				return EINVAL;

		memcpy(scan_codes, data, sizeof(pccons_keymap_t[KB_NUM_KEYS]));
		return 0;
	}
	case CONSOLE_GET_KEYMAP:
		if (!data)
			return EINVAL;
		memcpy(scan_codes, data, sizeof(pccons_keymap_t[KB_NUM_KEYS]));
		return 0;

	default:
		return EPASSTHROUGH;
	}

#ifdef DIAGNOSTIC
	panic("pcioctl: impossible");
#endif
}

void
pcstart(struct tty *tp)
{
	struct clist *cl;
	int s, len;
	u_char buf[PCBURST];

	s = spltty();
	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
		goto out;
	tp->t_state |= TS_BUSY;
	splx(s);
	/*
	 * We need to do this outside spl since it could be fairly
	 * expensive and we don't want our serial ports to overflow.
	 */
	cl = &tp->t_outq;
	len = q_to_b(cl, buf, PCBURST);
	sput(buf, len);
	s = spltty();
	tp->t_state &= ~TS_BUSY;
	if (ttypull(tp)) {
		tp->t_state |= TS_TIMEOUT;
		callout_schedule(&tp->t_rstrt_ch, 1);
	}
out:
	splx(s);
}

/* ARGSUSED */
void pccons_common_cnattach(bus_space_tag_t crt_iot, bus_space_tag_t crt_memt,
    bus_space_tag_t kbd_iot, struct pccons_config *config)
{
	int maj;
	static struct consdev pccons = {
		NULL, NULL, pccngetc, pccnputc, pccnpollc, NULL, NULL,
		    NULL, NODEV, CN_NORMAL
	};

	/*
	 * For now, don't screw with it.
	 */
	/* crtat = 0; */

	pc_context_init(crt_iot, crt_memt, kbd_iot, config);

	/* locate the major number */
	maj = cdevsw_lookup_major(&pc_cdevsw);
	pccons.cn_dev = makedev(maj, 0);

	cn_tab = &pccons;
}

/* ARGSUSED */
void
pccnputc(dev_t dev, int c)
{
	u_char cc, oldkernel = kernel;

	kernel = 1;
	if (c == '\n') {
		sput("\r\n", 2);
	} else {
		cc = c;
		sput(&cc, 1);
	}
	kernel = oldkernel;
}

/* ARGSUSED */
int
pccngetc(dev_t dev)
{
	char *cp;

	if (pc_xmode > 0)
		return 0;

	do {
		/* wait for byte */
		while ((kbd_cmd_read_1() & KBS_DIB) == 0);
		/* see if it's worthwhile */
		cp = sget();
	} while (!cp);
	if (*cp == '\r')
		return '\n';
	return *cp;
}

void
pccnpollc(dev_t dev, int on)
{

	polling = on;
	if (!on) {
		int unit;
		struct pc_softc *sc;
		int s;

		/*
		 * If disabling polling on a device that's been configured,
		 * make sure there are no bytes left in the FIFO, holding up
		 * the interrupt line.  Otherwise we won't get any further
		 * interrupts.
		 */
		unit = PCUNIT(dev);
		if (pc_cd.cd_ndevs > unit) {
			sc = device_lookup_private(&pc_cd, unit);
			if (sc != NULL) {
				s = spltty();
				pcintr(sc);
				splx(s);
			}
		}
	}
}

/*
 * Set line parameters.
 */
int
pcparam(struct tty *tp, struct termios *t)
{

	tp->t_ispeed = t->c_ispeed;
	tp->t_ospeed = t->c_ospeed;
	tp->t_cflag = t->c_cflag;
	return 0;
}

#define	wrtchar(c, at) do {\
	char *cp0 = (char *)crtat; *cp0++ = (c); *cp0 = (at); crtat++; vs.col++; \
} while (0)

/* translate ANSI color codes to standard pc ones */
static char fgansitopc[] = {
	FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE,
	FG_MAGENTA, FG_CYAN, FG_LIGHTGREY
};

static char bgansitopc[] = {
	BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE,
	BG_MAGENTA, BG_CYAN, BG_LIGHTGREY
};

static u_char iso2ibm437[] =
{
            0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,
         0xff,  0xad,  0x9b,  0x9c,     0,  0x9d,     0,  0x40,
         0x6f,  0x63,  0x61,  0xae,     0,     0,     0,     0,
         0xf8,  0xf1,  0xfd,  0x33,     0,  0xe6,     0,  0xfa,
            0,  0x31,  0x6f,  0xaf,  0xac,  0xab,     0,  0xa8,
         0x41,  0x41,  0x41,  0x41,  0x8e,  0x8f,  0x92,  0x80,
         0x45,  0x90,  0x45,  0x45,  0x49,  0x49,  0x49,  0x49,
         0x81,  0xa5,  0x4f,  0x4f,  0x4f,  0x4f,  0x99,  0x4f,
         0x4f,  0x55,  0x55,  0x55,  0x9a,  0x59,     0,  0xe1,
         0x85,  0xa0,  0x83,  0x61,  0x84,  0x86,  0x91,  0x87,
         0x8a,  0x82,  0x88,  0x89,  0x8d,  0xa1,  0x8c,  0x8b,
            0,  0xa4,  0x95,  0xa2,  0x93,  0x6f,  0x94,  0x6f,
         0x6f,  0x97,  0xa3,  0x96,  0x81,  0x98,     0,     0
};

/*
 * `pc3' termcap emulation.
 */
void
sput(const u_char *cp, int n)
{
	struct pccons_context *pc = &pccons_console_context;
	u_char c, scroll = 0;

	if (pc_xmode > 0)
		return;

	if (crtat == 0) {
		volatile u_short *dp;
		u_short was;
		unsigned cursorat;

		dp = bus_space_vaddr(pc->pc_crt_memt, pc->pc_cga_memh);
		was = *dp;
		*dp = 0xA55A;
		if (*dp != 0xA55A) {
			dp = bus_space_vaddr(pc->pc_crt_memt,
			    pc->pc_mono_memh);
			pc->pc_6845_ioh = pc->pc_mono_ioh;
			pc->pc_crt_memh = pc->pc_mono_memh;
			vs.color = 0;
		} else {
			*dp = was;
			pc->pc_6845_ioh = pc->pc_cga_ioh;
			pc->pc_crt_memh = pc->pc_cga_memh;
			vs.color = 1;
		}

#ifdef FAT_CURSOR
		cursor_shape = 0x0012;
#else
		get_cursor_shape();
#endif

		bios_display_info(&vs.col, &vs.row, &vs.ncol, &vs.nrow);
		vs.nchr = vs.ncol * vs.nrow;
		vs.col--;
		vs.row--;
		cursorat = vs.ncol * vs.row + vs.col;
		vs.at = FG_LIGHTGREY | BG_BLACK;

		Crtat = (u_short *)__UNVOLATILE(dp);
		crtat = Crtat + cursorat;

		if (vs.color == 0)
			vs.so_at = FG_BLACK | BG_LIGHTGREY;
		else
			vs.so_at = FG_YELLOW | BG_BLACK;

		fillw((vs.at << 8) | ' ', crtat, vs.nchr - cursorat);
	}

	while (n--) {
		if (!(c = *cp++))
			continue;

		switch (c) {
		case 0x1B:
			if (vs.state >= VSS_ESCAPE) {
				wrtchar(c, vs.so_at);
				vs.state = 0;
				goto maybe_scroll;
			} else
				vs.state = VSS_ESCAPE;
			break;

		case 0x9B:	/* CSI */
			vs.cx = vs.cy = 0;
			vs.state = VSS_EBRACE;
			break;

		case '\t': {
			int inccol = 8 - (vs.col & 7);
			crtat += inccol;
			vs.col += inccol;
		}
		maybe_scroll:
			if (vs.col >= vs.ncol) {
				vs.col -= vs.ncol;
				scroll = 1;
			}
			break;

		case '\b':
			if (crtat <= Crtat)
				break;
			--crtat;
			if (--vs.col < 0)
				vs.col += vs.ncol;	/* non-destructive backspace */
			break;

		case '\r':
			crtat -= vs.col;
			vs.col = 0;
			break;

		case '\n':
			crtat += vs.ncol;
			scroll = 1;
			break;

		default:
			switch (vs.state) {
			case 0:
				if (c == '\a')
					sysbeep(BEEP_FREQ, BEEP_TIME);
				else {
					/*
					 * If we're outputting multiple printed
					 * characters, just blast them to the
					 * screen until we reach the end of the
					 * buffer or a control character.  This
					 * saves time by short-circuiting the
					 * switch.
					 * If we reach the end of the line, we
					 * break to do a scroll check.
					 */
					for (;;) {
						if (c & 0x80)
							c = iso2ibm437[c&0x7f];

						if (vs.so)
							wrtchar(c, vs.so_at);
						else
							wrtchar(c, vs.at);
						if (vs.col >= vs.ncol) {
							vs.col = 0;
							scroll = 1;
							break;
						}
						if (!n || (c = *cp) < ' ')
							break;
						n--, cp++;
					}
				}
				break;
			case VSS_ESCAPE:
				switch (c) {
					case '[': /* Start ESC [ sequence */
						vs.cx = vs.cy = 0;
						vs.state = VSS_EBRACE;
						break;
					case 'c': /* Create screen & home */
						fillw((vs.at << 8) | ' ',
						    Crtat, vs.nchr);
						crtat = Crtat;
						vs.col = 0;
						vs.state = 0;
						break;
					case '7': /* save cursor pos */
						vs.offset = crtat - Crtat;
						vs.state = 0;
						break;
					case '8': /* restore cursor pos */
						crtat = Crtat + vs.offset;
						vs.row = vs.offset / vs.ncol;
						vs.col = vs.offset % vs.ncol;
						vs.state = 0;
						break;
					default: /* Invalid, clear state */
						wrtchar(c, vs.so_at);
						vs.state = 0;
						goto maybe_scroll;
				}
				break;

			default: /* VSS_EBRACE or VSS_EPARAM */
				switch (c) {
					int pos;
				case 'm':
					if (!vs.cx)
						vs.so = 0;
					else
						vs.so = 1;
					vs.state = 0;
					break;
				case 'A': { /* back cx rows */
					int cx = vs.cx;
					if (cx <= 0)
						cx = 1;
					else
						cx %= vs.nrow;
					pos = crtat - Crtat;
					pos -= vs.ncol * cx;
					if (pos < 0)
						pos += vs.nchr;
					crtat = Crtat + pos;
					vs.state = 0;
					break;
				}
				case 'B': { /* down cx rows */
					int cx = vs.cx;
					if (cx <= 0)
						cx = 1;
					else
						cx %= vs.nrow;
					pos = crtat - Crtat;
					pos += vs.ncol * cx;
					if (pos >= vs.nchr)
						pos -= vs.nchr;
					crtat = Crtat + pos;
					vs.state = 0;
					break;
				}
				case 'C': { /* right cursor */
					int cx = vs.cx,
					    col = vs.col;
					if (cx <= 0)
						cx = 1;
					else
						cx %= vs.ncol;
					pos = crtat - Crtat;
					pos += cx;
					col += cx;
					if (col >= vs.ncol) {
						pos -= vs.ncol;
						col -= vs.ncol;
					}
					vs.col = col;
					crtat = Crtat + pos;
					vs.state = 0;
					break;
				}
				case 'D': { /* left cursor */
					int cx = vs.cx,
					    col = vs.col;
					if (cx <= 0)
						cx = 1;
					else
						cx %= vs.ncol;
					pos = crtat - Crtat;
					pos -= cx;
					col -= cx;
					if (col < 0) {
						pos += vs.ncol;
						col += vs.ncol;
					}
					vs.col = col;
					crtat = Crtat + pos;
					vs.state = 0;
					break;
				}
				case 'J': /* Clear ... */
					switch (vs.cx) {
					case 0:
						/* ... to end of display */
						fillw((vs.at << 8) | ' ',
						    crtat,
						    Crtat + vs.nchr - crtat);
						break;
					case 1:
						/* ... to next location */
						fillw((vs.at << 8) | ' ',
						    Crtat,
						    crtat - Crtat + 1);
						break;
					case 2:
						/* ... whole display */
						fillw((vs.at << 8) | ' ',
						    Crtat,
						    vs.nchr);
						break;
					}
					vs.state = 0;
					break;
				case 'K': /* Clear line ... */
					switch (vs.cx) {
					case 0:
						/* ... current to EOL */
						fillw((vs.at << 8) | ' ',
						    crtat,
						    vs.ncol - vs.col);
						break;
					case 1:
						/* ... beginning to next */
						fillw((vs.at << 8) | ' ',
						    crtat - vs.col,
						    vs.col + 1);
						break;
					case 2:
						/* ... entire line */
						fillw((vs.at << 8) | ' ',
						    crtat - vs.col, vs.ncol);
						break;
					}
					vs.state = 0;
					break;
				case 'f': /* in system V consoles */
				case 'H': { /* Cursor move */
					int cx = vs.cx,
					    cy = vs.cy;
					if (!cx || !cy) {
						crtat = Crtat;
						vs.col = 0;
					} else {
						if (cx > vs.nrow)
							cx = vs.nrow;
						if (cy > vs.ncol)
							cy = vs.ncol;
						crtat = Crtat +
						    (cx - 1) * vs.ncol + cy - 1;
						vs.col = cy - 1;
					}
					vs.state = 0;
					break;
				}
				case 'M': { /* delete cx rows */
					u_short *crtAt = crtat - vs.col;
					int cx = vs.cx,
					    row = (crtAt - Crtat) / vs.ncol,
					    nrow = vs.nrow - row;
					if (cx <= 0)
						cx = 1;
					else if (cx > nrow)
						cx = nrow;
					if (cx < nrow)
#ifdef PCCONS_FORCE_WORD
						wcopy(crtAt + vs.ncol * cx,
						    crtAt, vs.ncol * (nrow -
						    cx) * CHR);
#else
						memmove(crtAt,
						    crtAt + vs.ncol * cx,
						    vs.ncol * (nrow - cx) *
						    CHR);
#endif
					fillw((vs.at << 8) | ' ',
					    crtAt + vs.ncol * (nrow - cx),
					    vs.ncol * cx);
					vs.state = 0;
					break;
				}
				case 'S': { /* scroll up cx lines */
					int cx = vs.cx;
					if (cx <= 0)
						cx = 1;
					else if (cx > vs.nrow)
						cx = vs.nrow;
					if (cx < vs.nrow)
#ifdef PCCONS_FORCE_WORD
						wcopy(Crtat + vs.ncol * cx,
						    Crtat, vs.ncol * (vs.nrow -
						    cx) * CHR);
#else
						memmove(Crtat,
						    Crtat + vs.ncol * cx,
						    vs.ncol * (vs.nrow - cx) *
						    CHR);
#endif
					fillw((vs.at << 8) | ' ',
					    Crtat + vs.ncol * (vs.nrow - cx),
					    vs.ncol * cx);
					/* crtat -= vs.ncol * cx; XXX */
					vs.state = 0;
					break;
				}
				case 'L': { /* insert cx rows */
					u_short *crtAt = crtat - vs.col;
					int cx = vs.cx,
					    row = (crtAt - Crtat) / vs.ncol,
					    nrow = vs.nrow - row;
					if (cx <= 0)
						cx = 1;
					else if (cx > nrow)
						cx = nrow;
					if (cx < nrow)
#ifdef PCCONS_FORCE_WORD
						wcopy(crtAt,
						    crtAt + vs.ncol * cx,
						    vs.ncol * (nrow - cx) *
						    CHR);
#else
						memmove(crtAt + vs.ncol * cx,
						    crtAt,
						    vs.ncol * (nrow - cx) *
						    CHR);
#endif
					fillw((vs.at << 8) | ' ', crtAt,
					    vs.ncol * cx);
					vs.state = 0;
					break;
				}
				case 'T': { /* scroll down cx lines */
					int cx = vs.cx;
					if (cx <= 0)
						cx = 1;
					else if (cx > vs.nrow)
						cx = vs.nrow;
					if (cx < vs.nrow)
#ifdef PCCONS_FORCE_WORD
						wcopy(Crtat,
						    Crtat + vs.ncol * cx,
						    vs.ncol * (vs.nrow - cx) *
						    CHR);
#else
						memmove(Crtat + vs.ncol * cx,
						    Crtat,
						    vs.ncol * (vs.nrow - cx) *
						    CHR);
#endif
					fillw((vs.at << 8) | ' ', Crtat,
					    vs.ncol * cx);
					/* crtat += vs.ncol * cx; XXX */
					vs.state = 0;
					break;
				}
				case ';': /* Switch params in cursor def */
					vs.state = VSS_EPARAM;
					break;
				case 'r':
					vs.so_at = (vs.cx & FG_MASK) |
					    ((vs.cy << 4) & BG_MASK);
					vs.state = 0;
					break;
				case 's': /* save cursor pos */
					vs.offset = crtat - Crtat;
					vs.state = 0;
					break;
				case 'u': /* restore cursor pos */
					crtat = Crtat + vs.offset;
					vs.row = vs.offset / vs.ncol;
					vs.col = vs.offset % vs.ncol;
					vs.state = 0;
					break;
				case 'x': /* set attributes */
					switch (vs.cx) {
					case 0:
						vs.at = FG_LIGHTGREY | BG_BLACK;
						break;
					case 1:
						/* ansi background */
						if (!vs.color)
							break;
						vs.at &= FG_MASK;
						vs.at |= bgansitopc[vs.cy & 7];
						break;
					case 2:
						/* ansi foreground */
						if (!vs.color)
							break;
						vs.at &= BG_MASK;
						vs.at |= fgansitopc[vs.cy & 7];
						break;
					case 3:
						/* pc text attribute */
						if (vs.state >= VSS_EPARAM)
							vs.at = vs.cy;
						break;
					}
					vs.state = 0;
					break;

				default: /* Only numbers valid here */
					if ((c >= '0') && (c <= '9')) {
						if (vs.state >= VSS_EPARAM) {
							vs.cy *= 10;
							vs.cy += c - '0';
						} else {
							vs.cx *= 10;
							vs.cx += c - '0';
						}
					} else
						vs.state = 0;
					break;
				}
				break;
			}
		}
		if (scroll) {
			scroll = 0;
			/* scroll check */
			if (crtat >= Crtat + vs.nchr) {
				if (!kernel) {
					int s = spltty();
					if (lock_state & KB_SCROLL)
						tsleep(&lock_state,
						    PUSER, "pcputc", 0);
					splx(s);
				}
#if PCCONS_FORCE_WORD
				wcopy(Crtat + vs.ncol, Crtat,
				    (vs.nchr - vs.ncol) * CHR);
#else
				memmove(Crtat, Crtat + vs.ncol,
				    (vs.nchr - vs.ncol) * CHR);
#endif
				fillw((vs.at << 8) | ' ',
				    Crtat + vs.nchr - vs.ncol,
				    vs.ncol);
				crtat -= vs.ncol;
			}
		}
	}
	async_update();
}

/* the unshifted code for KB_SHIFT keys is used by X to distinguish between
   left and right shift when reading the keyboard map */
static pccons_keymap_t	scan_codes[KB_NUM_KEYS] = {
/*  type       unshift   shift     control   altgr     shift_altgr scancode */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 0 unused */
  { KB_ASCII,  "\033",   "\033",   "\033",   "",       ""}, /* 1 ESCape */
  { KB_ASCII,  "1",      "!",      "!",      "",       ""}, /* 2 1 */
  { KB_ASCII,  "2",      "@",      "\000",   "",       ""}, /* 3 2 */
  { KB_ASCII,  "3",      "#",      "#",      "",       ""}, /* 4 3 */
  { KB_ASCII,  "4",      "$",      "$",      "",       ""}, /* 5 4 */
  { KB_ASCII,  "5",      "%",      "%",      "",       ""}, /* 6 5 */
  { KB_ASCII,  "6",      "^",      "\036",   "",       ""}, /* 7 6 */
  { KB_ASCII,  "7",      "&",      "&",      "",       ""}, /* 8 7 */
  { KB_ASCII,  "8",      "*",      "\010",   "",       ""}, /* 9 8 */
  { KB_ASCII,  "9",      "(",      "(",      "",       ""}, /* 10 9 */
  { KB_ASCII,  "0",      ")",      ")",      "",       ""}, /* 11 0 */
  { KB_ASCII,  "-",      "_",      "\037",   "",       ""}, /* 12 - */
  { KB_ASCII,  "=",      "+",      "+",      "",       ""}, /* 13 = */
  { KB_ASCII,  "\177",   "\177",   "\010",   "",       ""}, /* 14 backspace */
  { KB_ASCII,  "\t",     "\t",     "\t",     "",       ""}, /* 15 tab */
  { KB_ASCII,  "q",      "Q",      "\021",   "",       ""}, /* 16 q */
  { KB_ASCII,  "w",      "W",      "\027",   "",       ""}, /* 17 w */
  { KB_ASCII,  "e",      "E",      "\005",   "",       ""}, /* 18 e */
  { KB_ASCII,  "r",      "R",      "\022",   "",       ""}, /* 19 r */
  { KB_ASCII,  "t",      "T",      "\024",   "",       ""}, /* 20 t */
  { KB_ASCII,  "y",      "Y",      "\031",   "",       ""}, /* 21 y */
  { KB_ASCII,  "u",      "U",      "\025",   "",       ""}, /* 22 u */
  { KB_ASCII,  "i",      "I",      "\011",   "",       ""}, /* 23 i */
  { KB_ASCII,  "o",      "O",      "\017",   "",       ""}, /* 24 o */
  { KB_ASCII,  "p",      "P",      "\020",   "",       ""}, /* 25 p */
  { KB_ASCII,  "[",      "{",      "\033",   "",       ""}, /* 26 [ */
  { KB_ASCII,  "]",      "}",      "\035",   "",       ""}, /* 27 ] */
  { KB_ASCII,  "\r",     "\r",     "\n",     "",       ""}, /* 28 return */
  { KB_CTL,    "",       "",       "",       "",       ""}, /* 29 control */
  { KB_ASCII,  "a",      "A",      "\001",   "",       ""}, /* 30 a */
  { KB_ASCII,  "s",      "S",      "\023",   "",       ""}, /* 31 s */
  { KB_ASCII,  "d",      "D",      "\004",   "",       ""}, /* 32 d */
  { KB_ASCII,  "f",      "F",      "\006",   "",       ""}, /* 33 f */
  { KB_ASCII,  "g",      "G",      "\007",   "",       ""}, /* 34 g */
  { KB_ASCII,  "h",      "H",      "\010",   "",       ""}, /* 35 h */
  { KB_ASCII,  "j",      "J",      "\n",     "",       ""}, /* 36 j */
  { KB_ASCII,  "k",      "K",      "\013",   "",       ""}, /* 37 k */
  { KB_ASCII,  "l",      "L",      "\014",   "",       ""}, /* 38 l */
  { KB_ASCII,  ";",      ":",      ";",      "",       ""}, /* 39 ; */
  { KB_ASCII,  "'",      "\"",     "'",      "",       ""}, /* 40 ' */
  { KB_ASCII,  "`",      "~",      "`",      "",       ""}, /* 41 ` */
  { KB_SHIFT,  "\001",   "",       "",       "",       ""}, /* 42 shift */
  { KB_ASCII,  "\\",     "|",      "\034",   "",       ""}, /* 43 \ */
  { KB_ASCII,  "z",      "Z",      "\032",   "",       ""}, /* 44 z */
  { KB_ASCII,  "x",      "X",      "\030",   "",       ""}, /* 45 x */
  { KB_ASCII,  "c",      "C",      "\003",   "",       ""}, /* 46 c */
  { KB_ASCII,  "v",      "V",      "\026",   "",       ""}, /* 47 v */
  { KB_ASCII,  "b",      "B",      "\002",   "",       ""}, /* 48 b */
  { KB_ASCII,  "n",      "N",      "\016",   "",       ""}, /* 49 n */
  { KB_ASCII,  "m",      "M",      "\r",     "",       ""}, /* 50 m */
  { KB_ASCII,  ",",      "<",      "<",      "",       ""}, /* 51 , */
  { KB_ASCII,  ".",      ">",      ">",      "",       ""}, /* 52 . */
  { KB_ASCII,  "/",      "?",      "\037",   "",       ""}, /* 53 / */
  { KB_SHIFT,  "\002",   "",       "",       "",       ""}, /* 54 shift */
  { KB_KP,     "*",      "*",      "*",      "",       ""}, /* 55 kp * */
  { KB_ALT,    "",       "",       "",       "",       ""}, /* 56 alt */
  { KB_ASCII,  " ",      " ",      "\000",   "",       ""}, /* 57 space */
  { KB_CAPS,   "",       "",       "",       "",       ""}, /* 58 caps */
  { KB_FUNC,   "\033[M", "\033[Y", "\033[k", "",       ""}, /* 59 f1 */
  { KB_FUNC,   "\033[N", "\033[Z", "\033[l", "",       ""}, /* 60 f2 */
  { KB_FUNC,   "\033[O", "\033[a", "\033[m", "",       ""}, /* 61 f3 */
  { KB_FUNC,   "\033[P", "\033[b", "\033[n", "",       ""}, /* 62 f4 */
  { KB_FUNC,   "\033[Q", "\033[c", "\033[o", "",       ""}, /* 63 f5 */
  { KB_FUNC,   "\033[R", "\033[d", "\033[p", "",       ""}, /* 64 f6 */
  { KB_FUNC,   "\033[S", "\033[e", "\033[q", "",       ""}, /* 65 f7 */
  { KB_FUNC,   "\033[T", "\033[f", "\033[r", "",       ""}, /* 66 f8 */
  { KB_FUNC,   "\033[U", "\033[g", "\033[s", "",       ""}, /* 67 f9 */
  { KB_FUNC,   "\033[V", "\033[h", "\033[t", "",       ""}, /* 68 f10 */
  { KB_NUM,    "",       "",       "",       "",       ""}, /* 69 num lock */
  { KB_SCROLL, "",       "",       "",       "",       ""}, /* 70 scroll lock */
  { KB_KP,     "7",      "\033[H", "7",      "",       ""}, /* 71 kp 7 */
  { KB_KP,     "8",      "\033[A", "8",      "",       ""}, /* 72 kp 8 */
  { KB_KP,     "9",      "\033[I", "9",      "",       ""}, /* 73 kp 9 */
  { KB_KP,     "-",      "-",      "-",      "",       ""}, /* 74 kp - */
  { KB_KP,     "4",      "\033[D", "4",      "",       ""}, /* 75 kp 4 */
  { KB_KP,     "5",      "\033[E", "5",      "",       ""}, /* 76 kp 5 */
  { KB_KP,     "6",      "\033[C", "6",      "",       ""}, /* 77 kp 6 */
  { KB_KP,     "+",      "+",      "+",      "",       ""}, /* 78 kp + */
  { KB_KP,     "1",      "\033[F", "1",      "",       ""}, /* 79 kp 1 */
  { KB_KP,     "2",      "\033[B", "2",      "",       ""}, /* 80 kp 2 */
  { KB_KP,     "3",      "\033[G", "3",      "",       ""}, /* 81 kp 3 */
  { KB_KP,     "0",      "\033[L", "0",      "",       ""}, /* 82 kp 0 */
  { KB_KP,     ",",      "\177",   ",",      "",       ""}, /* 83 kp , */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 84 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 85 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 86 0 */
  { KB_FUNC,   "\033[W", "\033[i", "\033[u", "",       ""}, /* 87 f11 */
  { KB_FUNC,   "\033[X", "\033[j", "\033[v", "",       ""}, /* 88 f12 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 89 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 90 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 91 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 92 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 93 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 94 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 95 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 96 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 97 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 98 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 99 0 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 100 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 101 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 102 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 103 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 104 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 105 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 106 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 107 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 108 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 109 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 110 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 111 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 112 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 113 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 114 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 115 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 116 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 117 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 118 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 119 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 120 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 121 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 122 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 123 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 124 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 125 */
  { KB_NONE,   "",       "",       "",       "",       ""}, /* 126 */
  { KB_NONE,   "",       "",       "",       "",       ""}  /* 127 */
};

/*
 * Get characters from the keyboard.  If none are present, return NULL.
 */
char *
sget(void)
{
	u_char dt;
	static u_char extended = 0, shift_state = 0;
	static u_char capchar[2];

top:
	KBD_DELAY;
	dt = kbd_data_read_1();

	switch (dt) {
	case KBR_ACK: case KBR_ECHO:
		kb_oq_get = (kb_oq_get + 1) & 7;
		if(kb_oq_get != kb_oq_put) {
			kbd_data_write_1(kb_oq[kb_oq_get]);
		}
		goto loop;
	case KBR_RESEND:
		kbd_data_write_1(kb_oq[kb_oq_get]);
		goto loop;
	}

	if (pc_xmode > 0) {
#if defined(DDB) && defined(XSERVER_DDB)
		/* F12 enters the debugger while in X mode */
		if (dt == 88)
			Debugger();
#endif
		capchar[0] = dt;
		capchar[1] = 0;
		/*
		 * Check for locking keys.
		 *
		 * XXX Setting the LEDs this way is a bit bogus.  What if the
		 * keyboard has been remapped in X?
		 */
		switch (scan_codes[dt & 0x7f].type) {
		case KB_NUM:
			if (dt & 0x80) {
				shift_state &= ~KB_NUM;
				break;
			}
			if (shift_state & KB_NUM)
				break;
			shift_state |= KB_NUM;
			lock_state ^= KB_NUM;
			async_update();
			break;
		case KB_CAPS:
			if (dt & 0x80) {
				shift_state &= ~KB_CAPS;
				break;
			}
			if (shift_state & KB_CAPS)
				break;
			shift_state |= KB_CAPS;
			lock_state ^= KB_CAPS;
			async_update();
			break;
		case KB_SCROLL:
			if (dt & 0x80) {
				shift_state &= ~KB_SCROLL;
				break;
			}
			if (shift_state & KB_SCROLL)
				break;
			shift_state |= KB_SCROLL;
			lock_state ^= KB_SCROLL;
			if ((lock_state & KB_SCROLL) == 0)
				wakeup((void *)&lock_state);
			async_update();
			break;
		}
		return capchar;
	}

	switch (dt) {
	case KBR_EXTENDED:
		extended = 1;
		goto loop;
	}

#ifdef DDB
	/*
	 * Check for cntl-alt-esc.
	 */
	if ((dt == 1) && (shift_state & (KB_CTL | KB_ALT)) == (KB_CTL | KB_ALT)) {
		/* XXX - check pccons_is_console */
		Debugger();
		dt |= 0x80;	/* discard esc (ddb discarded ctl-alt) */
	}
#endif

	/*
	 * Check for make/break.
	 */
	if (dt & 0x80) {
		/*
		 * break
		 */
		dt &= 0x7f;
		switch (scan_codes[dt].type) {
		case KB_NUM:
			shift_state &= ~KB_NUM;
			break;
		case KB_CAPS:
			shift_state &= ~KB_CAPS;
			break;
		case KB_SCROLL:
			shift_state &= ~KB_SCROLL;
			break;
		case KB_SHIFT:
			shift_state &= ~KB_SHIFT;
			break;
		case KB_ALT:
			if (extended)
				shift_state &= ~KB_ALTGR;
			else
				shift_state &= ~KB_ALT;
			break;
		case KB_CTL:
			shift_state &= ~KB_CTL;
			break;
		}
	} else {
		/*
		 * make
		 */
		switch (scan_codes[dt].type) {
		/*
		 * locking keys
		 */
		case KB_NUM:
			if (shift_state & KB_NUM)
				break;
			shift_state |= KB_NUM;
			lock_state ^= KB_NUM;
			async_update();
			break;
		case KB_CAPS:
			if (shift_state & KB_CAPS)
				break;
			shift_state |= KB_CAPS;
			lock_state ^= KB_CAPS;
			async_update();
			break;
		case KB_SCROLL:
			if (shift_state & KB_SCROLL)
				break;
			shift_state |= KB_SCROLL;
			lock_state ^= KB_SCROLL;
			if ((lock_state & KB_SCROLL) == 0)
				wakeup((void *)&lock_state);
			async_update();
			break;
		/*
		 * non-locking keys
		 */
		case KB_SHIFT:
			shift_state |= KB_SHIFT;
			break;
		case KB_ALT:
			if (extended)
				shift_state |= KB_ALTGR;
			else
				shift_state |= KB_ALT;
			break;
		case KB_CTL:
			shift_state |= KB_CTL;
			break;
		case KB_ASCII:
			/* control has highest priority */
			if (shift_state & KB_CTL)
				capchar[0] = scan_codes[dt].ctl[0];
			else if (shift_state & KB_ALTGR) {
				if (shift_state & KB_SHIFT)
					capchar[0] = scan_codes[dt].shift_altgr[0];
				else
					capchar[0] = scan_codes[dt].altgr[0];
			}
			else {
				if (shift_state & KB_SHIFT)
					capchar[0] = scan_codes[dt].shift[0];
				else
					capchar[0] = scan_codes[dt].unshift[0];
			}
			if ((lock_state & KB_CAPS) && capchar[0] >= 'a' &&
			    capchar[0] <= 'z') {
				capchar[0] -= ('a' - 'A');
			}
			capchar[0] |= (shift_state & KB_ALT);
			extended = 0;
			return capchar;
		case KB_NONE:
printf("keycode %d\n",dt);
			break;
		case KB_FUNC: {
			char *more_chars;
			if (shift_state & KB_SHIFT)
				more_chars = scan_codes[dt].shift;
			else if (shift_state & KB_CTL)
				more_chars = scan_codes[dt].ctl;
			else
				more_chars = scan_codes[dt].unshift;
			extended = 0;
			return more_chars;
		}
		case KB_KP: {
			char *more_chars;
			if (shift_state & (KB_SHIFT | KB_CTL) ||
			    (lock_state & KB_NUM) == 0 || extended)
				more_chars = scan_codes[dt].shift;
			else
				more_chars = scan_codes[dt].unshift;
			extended = 0;
			return more_chars;
		}
		}
	}

	extended = 0;
loop:
	if ((kbd_cmd_read_1() & KBS_DIB) == 0)
		return 0;
	goto top;
}

paddr_t
pcmmap(dev_t dev, off_t offset, int nprot)
{
	struct pccons_context *pc = &pccons_console_context;
	paddr_t pa;

	if (offset >= 0xa0000 && offset < 0xc0000) {
		if (bus_space_paddr(pc->pc_crt_memt, pc->pc_mono_memh, &pa))
			return -1;
		pa += offset - pc->pc_config->pc_mono_memaddr;
		return mips_btop(pa);
	}
	if (offset >= 0x0000 && offset < 0x10000) {
		if (bus_space_paddr(pc->pc_crt_iot, pc->pc_mono_ioh, &pa))
			return -1;
		pa += offset - pc->pc_config->pc_mono_iobase;
		return mips_btop(pa);
	}
	if (offset >= 0x40000000 && offset < 0x40800000) {
		if (bus_space_paddr(pc->pc_crt_memt, pc->pc_mono_memh, &pa))
			return (-1);
		pa += offset - 0x40000000 - pc->pc_config->pc_mono_memaddr;
		return mips_btop(pa);
	}
	return -1;
}

void
pc_xmode_on(void)
{
	if (pc_xmode)
		return;
	pc_xmode = 1;

#ifdef XFREE86_BUG_COMPAT
	/* If still unchanged, get current shape. */
	if (cursor_shape == 0xffff)
		get_cursor_shape();
#endif
}

void
pc_xmode_off(void)
{
	if (pc_xmode == 0)
		return;
	pc_xmode = 0;

#ifdef XFREE86_BUG_COMPAT
	/* XXX It would be hard to justify why the X server doesn't do this. */
	set_cursor_shape();
#endif
	async_update();
}
