/*	$Id: graphcons.c,v 1.1.1.1 2006/08/23 17:03:05 pefo Exp $ */

/*
 * Copyright (c) 2005 Opsycon AB  (www.opsycon.se)
 * 
 * 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.
 *
 * 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 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.
 *
 */
/*-
 * Copyright (c) 1993, 1994, 1995 Charles Hannum.  All rights reserved.
 * 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.
 *
 * Copyright (c) 1994 Charles 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
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <termio.h>

#include <machine/pio.h>

#include <pmon/dev/graphcons.h>
#include <pmon.h>

#include <target/init_ati.h>

struct attr {
	int	fg;
	int	bg;
};

static struct video_state {
	int 	cx, cy;		/* escape parameters */
	int 	row, col;	/* current cursor position */
	int 	nrow, ncol, nchr;	/* current screen geometry */
	int	srow, scol;	/* Saved cursor pos */
	int	cursor;
#define	CURSOR_ON	0x0001
#define	CURSOR_BLINK	0x0002
	int	scrsaver;
#define	SCRSAVEDLY	5*60*TIMEOUTRATE
#define	SCROFFDLY	30*60*TIMEOUTRATE
#define	TIMEOUTRATE	4	/* Times per second timeout is run */
	struct timeout	timer;
	u_char	state;		/* parser state */
#define	VSS_ESCAPE	1
#define	VSS_EBRACE	2
#define	VSS_EPARAM	3
	int	so;		/* in standout mode? */
	struct attr	at;		/* normal attributes */
	struct attr	so_at;		/* standout attributes */
} vs;

/* Color attributes for foreground text */

#define BLACK                   0
#define BLUE                    1
#define GREEN                   2
#define CYAN                    3
#define RED                     4
#define MAGENTA                 5
#define BROWN                   6
#define LIGHTGREY               7
#define DARKGREY                8
#define LIGHTBLUE               9
#define LIGHTGREEN             10
#define LIGHTCYAN              11
#define LIGHTRED               12
#define LIGHTMAGENTA           13
#define YELLOW                 14
#define WHITE                  15
#define BLINK                0x80
#define MASK                 0x8f

/* key types */

#define KB_SCROLL       0x0001  /* stop output */
#define KB_NUM          0x0002  /* numeric shift  cursors vs. numeric */
#define KB_CAPS         0x0004  /* caps shift -- swaps case of letter */
#define KB_SHIFT        0x0008  /* keyboard shift */
#define KB_CTL          0x0010  /* control shift  -- allows ctl function */
#define KB_ASCII        0x0020  /* ascii code for this key */
#define KB_CASCII	0x0040  /* ascii code for this key */
#define KB_ALT          0x0080  /* alternate shift -- alternate chars */
#define KB_ALTGR        0x0100  /* alternate graphics shift */
#define KB_FUNC         0x0200  /* function key */
#define KB_KP           0x0400  /* Keypad keys */
#define KB_NONE         0x0800  /* no function */

#define KB_CODE_SIZE    4       /* Use a max of 4 for now... */
#define KB_NUM_KEYS     128     /* Number of scan codes */

typedef struct {
	u_short type;
	char unshift[KB_CODE_SIZE];
	char shift[KB_CODE_SIZE];
	char ctl[KB_CODE_SIZE];
	char altgr[KB_CODE_SIZE];
	char shift_altgr[KB_CODE_SIZE];
} kbd_keymap_t;

/* Keyboard controller status port */
#define  KBS_DIB        0x01    /* data in buffer */
#define  KBS_IBF        0x02    /* input buffer low */
#define  KBS_WARM       0x04    /* input buffer low */
#define  KBS_OCMD       0x08    /* output buffer has command */
#define  KBS_NOSEC      0x10    /* security lock not engaged */
#define  KBS_TERR       0x20    /* transmission error */
#define  KBS_RERR       0x40    /* receive error */
#define  KBS_PERR       0x80    /* parity error */

#define K_RDCMDBYTE     0x20
#define K_LDCMDBYTE     0x60

#define KC8_TRANS       0x40    /* convert to old scan codes */
#define KC8_MDISABLE    0x20    /* disable mouse */
#define KC8_KDISABLE    0x10    /* disable keyboard */
#define KC8_IGNSEC      0x08    /* ignore security lock */
#define KC8_CPU         0x04    /* exit from protected mode reset */
#define KC8_MENABLE     0x02    /* enable mouse interrupt */
#define KC8_KENABLE     0x01    /* enable keyboard interrupt */
#define CMDBYTE         (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE)

/* keyboard commands */
#define KBC_RESET       0xFF    /* reset the keyboard */
#define KBC_RESEND      0xFE    /* request the keyboard resend the last byte */
#define KBC_SETDEFAULT  0xF6    /* resets keyboard to its power-on defaults */
#define KBC_DISABLE     0xF5    /* as per KBC_SETDEFAULT, but also disable key s
canning */
#define KBC_ENABLE      0xF4    /* enable key scanning */
#define KBC_TYPEMATIC   0xF3    /* set typematic rate and delay */
#define KBC_SETTABLE    0xF0    /* set scancode translation table */
#define KBC_MODEIND     0xED    /* set mode indicators (i.e. LEDs) */
#define KBC_ECHO        0xEE    /* request an echo from the keyboard */
#define KBC_SELFTEST	0xaa    /* initiate self-test */

/* 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	KBC_SELFTEST_OK	0x55

static u_char lock_state = KB_NUM, old_lock_state = 0xff;
int kbd_awake;
long kbd_cmdp, kbd_datap;


int kbc_wakeup(void);
int kbc_cmd(u_char);
int kbd_cmd(u_char);
void kbd_update(void);
void video_sput(u_char);
char *video_sget(void);

/* translate ANSI color codes to standard pc ones */
static char ansitopc[] = {
        BLACK, RED, GREEN, BROWN, BLUE, MAGENTA, CYAN, LIGHTGREY
};

static inline void
wrtchar(char ch)
{
	if (vs.so)
		ati_render_char(ch, vs.col, vs.row, vs.so_at.fg, vs.so_at.bg);
	else
		ati_render_char(ch, vs.col, vs.row, vs.at.fg, vs.at.bg);
	vs.col++;
}


static __inline int
kbd_wait_output(void)
{
	u_int i;

	for (i = 1000000; i; i--)
		if ((inb(kbd_cmdp) & KBS_IBF) == 0) {
			delay(10);
			return 1;
		}
	return 0;
}

static __inline int
kbd_wait_input(void)
{
	u_int i;

	for (i = 1000000; i; i--)
		if ((inb(kbd_cmdp) & KBS_DIB) != 0) {
			delay(10);
			return 1;
		}
	return 0;
}

static __inline void
kbd_flush_input(void)
{
	u_char c;

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

static u_char
kbc_get8042cmd(void)
{
	if (!kbd_wait_output())
		return -1;
	outb(kbd_cmdp, K_RDCMDBYTE);
	if (!kbd_wait_input())
		return -1;
	return inb(kbd_datap);
}

static int
kbc_put8042cmd(u_char val)
{
	if (!kbd_wait_output())
		return 0;
	outb(kbd_cmdp, K_LDCMDBYTE);
	if (!kbd_wait_output())
		return 0;
	outb(kbd_datap, val);
	return 1;
}

int
kbc_wakeup()
{
	kbd_cmdp = (long)PHYS_TO_UNCACHED(0x14000064);
	kbd_datap = (long)PHYS_TO_UNCACHED(0x14000060);

	kbc_cmd(KBC_SELFTEST);

	outb(kbd_cmdp, 0xcb);
	while (inb(kbd_cmdp) & KBS_IBF)
		;
	outb(kbd_datap, 0x01);

	kbc_put8042cmd(KC8_TRANS|KC8_CPU|KC8_KENABLE);

	kbc_cmd(0xae);

	kbd_flush_input();
	if (kbd_cmd(KBC_RESET)) {
		kbd_update();
		kbd_awake = 1;
		return 1;
	}
	return 0;
}

int
kbc_cmd(u_char val)
{
	if (!kbd_wait_output())
		return 0;
	outb(kbd_cmdp, val);
	
	if (kbd_wait_input()) {
		int c;
		delay(100);
		if ((c = inb(kbd_datap)) == KBC_SELFTEST_OK)
			return 1;
	}
	return 0;
}

int
kbd_cmd(u_char val)
{
	u_int retries = 3;

	do {
		if (!kbd_wait_output())
			return 0;
		outb(kbd_datap, val);

		if (kbd_wait_input()) {
			delay(100);
			switch (inb(kbd_datap)) {
			case KBR_ACK:
			case KBR_ECHO:
				return 1;
			}
		}
	} while (--retries);
	return 0;
}

void
kbd_update()
{
	if (lock_state != old_lock_state) {
		old_lock_state = lock_state;
		if (!kbd_cmd(KBC_MODEIND) ||
		    !kbd_cmd(lock_state)) {
			(void) kbd_cmd(KBC_ENABLE);
		}
	}
}

/*
 * `pc3' termcap emulation.
 */
static void
video_timeout(void *p)
{
	if (vs.scrsaver <= 0) {
		/* When time have elapsed, turn off CRT */
		ati_dsp_enable(0);
	} else if (vs.scrsaver < (SCROFFDLY - SCRSAVEDLY)) {
		/* Move around splash every 10 seconds */
		if ((vs.scrsaver % (10 * TIMEOUTRATE)) == 0)
			ati_dsp_enable(2);
	}
	vs.scrsaver--;

	/* Blinking cursor */
	if (vs.cursor & CURSOR_ON) {
		if (!(vs.cursor & CURSOR_BLINK) && (vs.scrsaver % 3) == 0) {
			vs.cursor |= CURSOR_BLINK;
			ati_cursor(vs.col, vs.row, vs.at.bg, vs.at.fg);
		} else if ((vs.cursor & CURSOR_BLINK) && (vs.scrsaver % 3) == 2) {
			vs.cursor &= ~CURSOR_BLINK;
			ati_cursor(vs.col, vs.row, vs.at.fg, vs.at.bg);
		}
	}

	timeout_add(&vs.timer, hz/TIMEOUTRATE);
}

static void
fillch(char ch, int row, int col, int nchar)
{
	int scol, srow;

	scol = vs.col;
	srow = vs.row;
	vs.col = col;
	vs.row = row;
	while (nchar--) {
		if (vs.col > vs.ncol) {
			vs.col = 0;
			vs.row++;
			if (vs.row > vs.nrow)
				break;
		}
		wrtchar(ch);
	}
	vs.col = scol;
	vs.row = srow;
}

void
video_sput(u_char ch)
{
	int c = ch, scroll = 0;

	if (vs.ncol == 0) {

		vs.ncol = 80;
		vs.nrow = 48;
		vs.nchr = vs.ncol * vs.nrow;
		vs.col = 0;
		vs.row = 0;
		vs.at.fg = LIGHTGREY;
		vs.at.bg = BLACK;
		vs.so_at.fg = YELLOW;
		vs.so_at.bg = BLACK;

		fillch(' ', vs.row, vs.col, vs.nchr - (vs.ncol * vs.row));
		timeout_set(&vs.timer, video_timeout, &vs);
		timeout_add(&vs.timer, hz/TIMEOUTRATE);
	}

	switch (c) {
	case 0x1B:
		if (vs.state >= VSS_ESCAPE) {
			wrtchar(c); 
			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);
		vs.col += inccol;
	}
	maybe_scroll:
		if (vs.col >= vs.ncol) {
			vs.col -= vs.ncol;
			scroll = 1;
		}
		break;

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

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

	case '\n':
		scroll = 1;
		break;
	default:
		switch (vs.state) {
		case 0:
			if (c != '\a') {
				wrtchar(c);
				if (vs.col >= vs.ncol) {
					vs.col = 0;
					scroll = 1;
				}
			}
			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 */
					fillch(' ', 0, 0, vs.nchr);
					vs.col = 0;
					vs.row = 0;
					vs.state = 0;
					break;
				case '7': /* save cursor pos */
					vs.srow = vs.row;
					vs.scol = vs.col;
					vs.state = 0;
					break;
				case '8': /* restore cursor pos */
					vs.row = vs.srow;
					vs.col = vs.scol;
					vs.state = 0;
					break;
				default: /* Invalid, clear state */
					wrtchar(c); 
					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;
				vs.row -= cx;
				if (vs.row < 0)
					vs.row += vs.nrow;
				vs.state = 0;
				break;
			}
			case 'B': { /* down cx rows */
				int cx = vs.cx;
				if (cx <= 0)
					cx = 1;
				else
					cx %= vs.nrow;
				vs.row += cx;
				if (vs.row > vs.nrow)
					vs.row -= vs.nrow;
				vs.state = 0;
				break;
			}
			case 'C': { /* right cursor */
				int cx = vs.cx;
				if (cx <= 0)
					cx = 1;
				else
					cx %= vs.ncol;
				vs.col += cx;
				if (vs.col >= vs.ncol) {
					vs.col -= vs.ncol;
				}
				vs.state = 0;
				break;
			}
			case 'D': { /* left cursor */
				int cx = vs.cx;
				if (cx <= 0)
					cx = 1;
				else
					cx %= vs.ncol;
				vs.col -= cx;
				if (vs.col < 0) {
					vs.col += vs.ncol;
				}
				vs.state = 0;
				break;
			}
			case 'J': /* Clear ... */
				switch (vs.cx) {
				case 0:
					/* ... to end of display */
					fillch(' ', vs.row, vs.col,
					    vs.nchr - vs.row * vs.ncol - vs.col);
					break;
				case 1:
					/* ... to next location */
					fillch(' ', 0, 0,
					    vs.row * vs.ncol + vs.col);
					break;
				case 2:
					/* ... whole display */
					fillch(' ', 0, 0, vs.nchr);
					break;
				}
				vs.state = 0;
				break;
			case 'K': /* Clear line ... */
				switch (vs.cx) {
				case 0:
					/* ... current to EOL */
					fillch(' ', vs.row, vs.col,
					    vs.ncol - vs.col);
					break;
				case 1:
					/* ... beginning to next */
					fillch(' ', vs.row, 0,
					    vs.col);
					break;
				case 2:
					/* ... entire line */
					fillch(' ', vs.row, 0, 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) {
					vs.col = 0;
					vs.row = 0;
				} else {
					if (cx > vs.nrow)
						cx = vs.nrow;
					if (cy > vs.ncol)
						cy = vs.ncol;
					vs.col = cy - 1;
					vs.row = cx;
				}
				vs.state = 0;
				break;
			}
#if NOTYET
			case 'M': { /* delete cx rows */
				u_int16_t *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)
					bcopy(crtAt + vs.ncol * cx,
					    crtAt, vs.ncol * (nrow -
					    cx));
				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)
					bcopy(Crtat + vs.ncol * cx,
					    Crtat, vs.ncol * (vs.nrow -
					    cx));
				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_int16_t *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)
					bcopy(crtAt,
					    crtAt + vs.ncol * cx,
					    vs.ncol * (nrow - cx));
				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)
					bcopy(Crtat,
					    Crtat + vs.ncol * cx,
					    vs.ncol * (vs.nrow - cx));
				fillw((vs.at << 8) | ' ', Crtat,
				    vs.ncol * cx);
				/* crtat += vs.ncol * cx; XXX */
				vs.state = 0;
				break;
			}
#endif
			case ';': /* Switch params in cursor def */
				vs.state = VSS_EPARAM;
				break;
			case 'r':
				vs.so_at.fg = vs.cx & 15;
				vs.so_at.bg = vs.cy & 15;
				vs.state = 0;
				break;
			case 's': /* save cursor pos */
				vs.srow = vs.row;
				vs.scol = vs.col;
				vs.state = 0;
				break;
			case 'u': /* restore cursor pos */
				vs.row = vs.srow;
				vs.col = vs.scol;
				vs.state = 0;
				break;
			case 'x': /* set attributes */
				switch (vs.cx) {
				case 0:
					vs.at.fg = LIGHTGREY;
					vs.at.bg = BLACK;
					break;
				case 1:
					/* ansi background */
					vs.at.bg = ansitopc[vs.cy & 7];
					break;
				case 2:
					/* ansi foreground */
					vs.at.fg = ansitopc[vs.cy & 7];
					break;
				case 3:
					/* pc text attribute */
					if (vs.state >= VSS_EPARAM)
						vs.at.fg = vs.cy & 15;
						vs.at.bg = (vs.cy >> 4) & 15;
					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 */
		vs.row++;
		if (vs.row >= vs.nrow) {
			vs.row = vs.nrow - 1;
			ati_scroll(0, 1, vs.nrow);
			fillch(' ', vs.row, 0, vs.ncol);
		}
	}
}

/* the unshifted code for KB_SHIFT keys is used by X to distinguish between
   left and right shift when reading the keyboard map */
kbd_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_CASCII, "q",      "Q",      "\021",   "",       ""}, /* 16 q */
  { KB_CASCII, "w",      "W",      "\027",   "",       ""}, /* 17 w */
  { KB_CASCII, "e",      "E",      "\005",   "",       ""}, /* 18 e */
  { KB_CASCII, "r",      "R",      "\022",   "",       ""}, /* 19 r */
  { KB_CASCII, "t",      "T",      "\024",   "",       ""}, /* 20 t */
  { KB_CASCII, "y",      "Y",      "\031",   "",       ""}, /* 21 y */
  { KB_CASCII, "u",      "U",      "\025",   "",       ""}, /* 22 u */
  { KB_CASCII, "i",      "I",      "\011",   "",       ""}, /* 23 i */
  { KB_CASCII, "o",      "O",      "\017",   "",       ""}, /* 24 o */
  { KB_CASCII, "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_CASCII, "a",      "A",      "\001",   "",       ""}, /* 30 a */
  { KB_CASCII, "s",      "S",      "\023",   "",       ""}, /* 31 s */
  { KB_CASCII, "d",      "D",      "\004",   "",       ""}, /* 32 d */
  { KB_CASCII, "f",      "F",      "\006",   "",       ""}, /* 33 f */
  { KB_CASCII, "g",      "G",      "\007",   "",       ""}, /* 34 g */
  { KB_CASCII, "h",      "H",      "\010",   "",       ""}, /* 35 h */
  { KB_CASCII, "j",      "J",      "\n",     "",       ""}, /* 36 j */
  { KB_CASCII, "k",      "K",      "\013",   "",       ""}, /* 37 k */
  { KB_CASCII, "l",      "L",      "\014",   "",       ""}, /* 38 l */
  { KB_ASCII,  ";",      ":",      ";",      "",       ""}, /* 39 ; */
  { KB_ASCII,  "'",      "\"",     "'",      "",       ""}, /* 40 ' */
  { KB_ASCII,  "`",      "~",      "`",      "",       ""}, /* 41 ` */
  { KB_SHIFT,  "\001",   "",       "",       "",       ""}, /* 42 shift */
  { KB_ASCII,  "\\",     "|",      "\034",   "",       ""}, /* 43 \ */
  { KB_CASCII, "z",      "Z",      "\032",   "",       ""}, /* 44 z */
  { KB_CASCII, "x",      "X",      "\030",   "",       ""}, /* 45 x */
  { KB_CASCII, "c",      "C",      "\003",   "",       ""}, /* 46 c */
  { KB_CASCII, "v",      "V",      "\026",   "",       ""}, /* 47 v */
  { KB_CASCII, "b",      "B",      "\002",   "",       ""}, /* 48 b */
  { KB_CASCII, "n",      "N",      "\016",   "",       ""}, /* 49 n */
  { KB_CASCII, "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 *
video_sget()
{
	u_char dt;
	u_char *chars;
	static u_char extended = 0, shift_state = 0;

top:
	delay(100);
	dt = inb(kbd_datap);

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

	if (dt & 0x80) {	/* Key release */
		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 {		/* Key depress */
		switch (scan_codes[dt].type) {
		case KB_NUM:
			if (shift_state & KB_NUM)
				break;
			shift_state |= KB_NUM;
			lock_state ^= KB_NUM;
			kbd_update();
			break;
		case KB_CAPS:
			if (shift_state & KB_CAPS)
				break;
			shift_state |= KB_CAPS;
			lock_state ^= KB_CAPS;
			kbd_update();
			break;
		case KB_SCROLL:
			if (shift_state & KB_SCROLL)
				break;
			shift_state |= KB_SCROLL;
			lock_state ^= KB_SCROLL;
			kbd_update();
			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;
		case KB_ASCII:
		case KB_CASCII:
			/* control has highest priority */
			if (shift_state & KB_CTL)
				chars = scan_codes[dt].ctl;
			else if (shift_state & KB_ALTGR) {
				if (shift_state & KB_SHIFT)
					chars = scan_codes[dt].shift_altgr;
				else
					chars = scan_codes[dt].altgr;
			}
			else {
				if (shift_state & KB_SHIFT)
					chars = scan_codes[dt].shift;
				else
					chars = scan_codes[dt].unshift;
			}
			if ((lock_state & KB_CAPS) &&
			    scan_codes[dt].type == KB_CASCII)
				chars = scan_codes[dt].shift;
			
//			capchar[0] |= (shift_state & KB_ALT);
			extended = 0;
			return chars;
		case KB_NONE:
			break;
		case KB_FUNC:
			if (shift_state & KB_SHIFT)
				chars = scan_codes[dt].shift;
			else if (shift_state & KB_CTL)
				chars = scan_codes[dt].ctl;
			else
				chars = scan_codes[dt].unshift;
			extended = 0;
			return chars;
		case KB_KP:
			if (shift_state & (KB_SHIFT | KB_CTL) ||
			    (lock_state & KB_NUM) == 0 || extended)
				chars = scan_codes[dt].shift;
			else
				chars = scan_codes[dt].unshift;
			extended = 0;
			return chars;
		}
	}

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

int
graphcons(int op, struct DevEntry *dev, unsigned long param, int data)
{
static char *cp = NULL;
	char c;

	switch(op) {
	case OP_INIT:
		kbd_cmdp = (long)PHYS_TO_UNCACHED(0x14000064);
		kbd_datap = (long)PHYS_TO_UNCACHED(0x14000060);


		break;
	case OP_XBAUD:
	case OP_BAUD:
		break;

	case OP_TXRDY:
		return !(lock_state & KB_SCROLL);

	case OP_TX:
		if ((vs.cursor & CURSOR_ON) && !(vs.cursor & CURSOR_BLINK))
			ati_cursor(vs.col, vs.row, vs.at.bg, vs.at.fg);
		vs.cursor &= ~(CURSOR_ON|CURSOR_BLINK);
		video_sput(data);
		ati_cursor(vs.col, vs.row, vs.at.fg, vs.at.bg);
		vs.cursor |= CURSOR_ON;
		ati_dsp_enable(1);
		vs.scrsaver = SCROFFDLY;
		break;

	case OP_RXRDY:
		if (cp != NULL)
			return 1;
		if (kbd_awake && inb(kbd_cmdp) & KBS_DIB) {
			ati_dsp_enable(1);
			vs.scrsaver = SCROFFDLY;
			cp = video_sget();
			if (cp != NULL)
				return 1;
		}
		break;

	case OP_RX:
		if (cp != NULL || kbd_awake) {
			if (cp == NULL)
				cp = video_sget();
			if (cp) {
				c = *cp++;
				if (*cp == '\0')
					cp = NULL;
				return c;
			}
		}
		return 0;
	}

	return 0;
}

