Coherent4.2.10/conf/vtnkb/src/vtnkb.c

Compare this file to the similar file:
Show the results in this format:

/* $Header: /ker/io.386/RCS/vtnkb.c,v 2.5 93/08/19 10:39:48 nigel Exp Locker: nigel $ */
/*
 * Keyboard driver, virtual consoles, loadable tables.
 *
 * $Log:	vtnkb.c,v $
 * Revision 2.5  93/08/19  10:39:48  nigel
 * r83 ioctl (), corefile, new headers
 * 
 * Revision 2.4  93/08/19  04:03:41  nigel
 * Nigel's R83
 */

#define SWANFIX 1
#define GREEKFIX 1

/*
 * User configurable AT keyboard / display driver.
 */

#include <common/_tricks.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <stddef.h>
#include <signal.h>

#include <kernel/trace.h>
#include <sys/coherent.h>
#include <sys/con.h>
#include <sys/tty.h>
#include <sys/seg.h>
#include <sys/sched.h>
#include <sys/kb.h>
#include <sys/devices.h>
#include <sys/silo.h>
#include <sys/vt.h>
#include <sys/vtkd.h>
#include <sys/uproc.h>

#define	KBDEBUG(x)	T_CON(1, printf(x));	/* debugging output */
#define	KBDEBUG2(x, y)	T_CON(1, printf(x, y));	/* debugging output */
#define	KBDEBUG3(x, y, z)	T_CON(1, printf(x, y, z));	/* debugging output */

/*
 * values for kbstate
 */
#define	KB_IDLE		0		/* nothing going on right now */
#define	KB_SINGLE	1		/* sent a single byte cmd to the kbd */
#define	KB_DOUBLE_1	2		/* sent 1st byte of 2-byte cmd to kbd */
#define	KB_DOUBLE_2	3		/* sent 2nd byte of 2-byte cmd to kbd */
#define TIMER_CTL    0x43                     /* Timer control */
#define TIMER_CNT    0x42                     /* Timer counter */
#define SPEAKER_CTL  0x61                     /* Speaker control */

/*
 * patchable params for non-standard keyboards
 */
int	KBDATA = 0x60;			/* Keyboard data */
int	KBCTRL = 0x61;			/* Keyboard control */
int	KBSTS_CMD = 0x64;		/* Keyboard status / command */
int	KBFLAG = 0x80;			/* Keyboard reset flag */
int	KBBOOT = 1;			/* 0: disallow reboot from keyboard */
int	KBTIMEOUT = 10000;		/* shouldn't need this much */
int	KBCMDBYTE = 0x05;		/* no translation */

/*
 * KBSTATUS bits
 */
#define	STS_OBUF_FULL	0x01		/* kbd output buffer full */
#define	STS_IBUF_FULL	0x02		/* kbd input buffer full */
#define	STS_SYSTEM	0x04
#define	STS_CMD_DATA	0x08		/* 1: command or status */
#define	STS_INHIBIT	0x10		/* 0: keyboard inhibited */
#define	STS_AUX_OBUF_FULL	0x20
#define	STS_TIMEOUT	0x40		/* general timeout */
#define	STS_PAR_ERR	0x80		/* parity error */

/*
 * The following are magic commands which read from or write to the
 * controller command byte. These get output to the KBSTS_CMD port.
 */
#define	C_READ_CMD	0x20		/* read controller command byte */
#define	C_WRITE_CMD	0x60		/* write controller command byte */
#define	C_TRANSLATE	0x40		/* translate enable bit in cmd byte */

/*
 * Globals:
 * The 286 keyboard mapping table is too large to fit into kernel data space,
 * so we need to allocate a segment to it.  386 is easy.
 * The function keys tend to be small and tend to change substantially
 * more often than the mapping table, so we keep them in the kernel data space.
 */
static	unsigned shift;			/* state of all shift / lock keys */
static	unsigned char	** funkeyp = 0;	/* ptr to array of func. keys ptrs */
static	FNKEY	* fnkeys = 0;		/* pointer to structure of values */
static	unsigned fklength;		/* length of function key text */
static	unsigned prev_cmd;		/* previous command sent to KBD */
static	unsigned cmd2;			/* 2nd byte of command to KBD */
static	unsigned sh_index;		/* shift / lock state index */
#ifdef _I386
static	KBTBL	kb [MAX_KEYS];		/* keyboard table */
#else
static	SEG	* kbsegp;		/* keyboard table segment */
#endif

/*
 * State variables.
 */
int		islock;			/* Keyboard locked flag */
int		isbusy;			/* Raw input conversion busy */
static	char	table_loaded;		/* true == keyboard table resident */
static	char	fk_loaded;		/* true == function keys resident */
static	int	kbstate = KB_IDLE;	/* current keyboard state */
static  int     xlate = 1;              /* scan code translation flag */

#define	ESCAPE_CHAR	'\x1B'
#define	ESCAPE_STRING	"\x1B"


static int X11led;


/*
 * Functions.
 */

int		vtmmstart ();
void		vtmmwrite	__PROTO ((o_dev_t _dev, IO * _iop,
					  __cred_t * _credp));
void		vtmmwatch	__PROTO((o_dev_t));


/*
==============================================================================
==============================================================================
*/
/* constants for vtdata [] */
#define VT_VGAPORT	0x3D4
#define VT_MONOPORT	0x3B4

#define VT_MONOBASE	(SEL_VIDEOa)
#define VT_VGABASE	(SEL_VIDEOb)

/*
	Patchable table entries,
	we go indirect in order to produce a label which can be addressed
*/
#if SWANFIX
int VTSWAN = 0;		/* patch to 1 for epstein's fix for Swan keyboard */
#endif

#if GREEKFIX
static void ToggleGreek ();
static int ToGreek ();
extern int VTGREEK;	/* patch to 1 for TECOP Greek mod */
#endif

/* Configurable variables - see ker / conf / console / Space.c */
extern int 	vga_count;
extern int 	mono_count;
extern int	sep_shift;

HWentry	VTVGA =		{ 4, 0, VT_VGAPORT, { 0, VT_VGABASE }, { 25, 80 } };
HWentry	VTMONO =	{ 4, 0, VT_MONOPORT, { 0, VT_MONOBASE }, { 25, 80 } };

HWentry	* vtHWtable [] = {
	& VTVGA,	/* VGA followed by MONO is compatible to DOS */
	& VTMONO
};
#define	vtHWend	(vtHWtable + __ARRAY_LENGTH (vtHWtable))

extern	int	vtmminit ();

static	VTDATA	const_vtdata	= {
	vtmminit,		/* func */
	0,			/* port */
	0,			/* seg */
	0,			/* off */
	0, 0,			/* rowl, col */
	0,			/* pos */
	0x07,			/* attr */
	0, 0, 0, 0, 0, 0, 0, 0,	/* args */
	0,			/* nargs */
	0, 24, 25,		/* brow, erow, lrow */
	0, 0,			/* srow, scol */
	0, 24,			/* ibrow, ierow */
	0,			/* invis */
	0,			/* slow */
	1,			/* wrap */
	0x07,			/* oattr */
	0,			/* font */
	0,			/* esc */
	0			/* visible */
};

/* later this should be dynamic */
VTDATA	* vtconsole, ** vtdata;

int	vtcount, vtmax;
extern	int	vtactive;
int	vt_verbose = { 0 };
int	vt_opened = { 0 };

/* Terminal structure. */
TTY	** vttty;

/*
==============================================================================
==============================================================================
*/

static silo_t in_silo;

/*
 * Given hw pointer for one of four types of adapters, see if
 * device is present by write / readback of video memory.
 *
 * return 1 if present, else 0
 */

int
hwpresent (hw)
HWentry * hw;
{
	int	save, present = 1;

	PRINTV ("hwpresent: %x:%x",
		hw->vidmemory.seg, hw->vidmemory.off);
	save = ffword (hw->vidmemory.off, hw->vidmemory.seg);

	sfword (hw->vidmemory.off, hw->vidmemory.seg, 0xAA55);
	if (ffword (hw->vidmemory.off, hw->vidmemory.seg) != 0xAA55)
		present = 0;

	sfword (hw->vidmemory.off, hw->vidmemory.seg, 0x55AA);
	if (ffword (hw->vidmemory.off, hw->vidmemory.seg) != 0x55AA)
		present = 0;

	sfword (hw->vidmemory.off, hw->vidmemory.seg, save);
	PRINTV ("%s present\n", present ? "" : " NOT");
	return present;
}


void
vtdatainit (vp)
VTDATA	* vp;
{
	/*
	 * vtdata init - vmm part
	 */
	vp->vmm_invis = 0;		/* cursor visible */

	vp->vt_buffer = kalloc (TEXTBLOCK);
	vp->vmm_seg = vp->vmm_mseg = vtds_sel ();
	vp->vmm_off = vp->vmm_moff = vp->vt_buffer;

	PRINTV ( "vt@%x init index %d,%d), seg %x, off %x\n",
		vp, vp->vt_ind, vp->vmm_mseg, vp->vmm_moff );
	/*
	 * vtdata init - vnkb part
	 */

	/* Make the first memory block active, if present */ 
	vp->vnkb_lastc = 0;
	vp->vnkb_fnkeys = 0;	
	vp->vnkb_funkeyp = 0;	
	vp->vnkb_fk_loaded = 0;			/* no Fn keys yet */
}


/*
 * Load entry point.
 */

void
isload ()
{
	int i;
	HWentry ** hw;
	VTDATA * vp;

	PRINTV ("vtload:\n");
	fk_loaded = 0;
	table_loaded = 0;
	kbstate = KB_IDLE;

	/* Sugar for idtune and kpatch. */
	VTVGA.count = vga_count;
	VTMONO.count = mono_count;

	/* figure out what our current max is */
	for (vtmax = 0, hw = vtHWtable ; hw != vtHWend ; hw ++) {
		vtmax += (* hw)->count;
		(* hw)->found = 0;	/* assume non-exist */
	}
	PRINTV ("vtload: %d screens possible\n", vtmax);

	vtdata = (VTDATA **) kalloc (vtmax * sizeof (* vtdata ));
	if (vtdata == NULL) {
		printf ("vtload: unable to obtain vtdata [%d]\n", vtmax);
		return;
	}

	PRINTV ("vtload: obtained vtdata [%d] @%x\n", vtmax, vtdata);

	vttty = (TTY **) kalloc (vtmax * sizeof (* vttty ));

	if (vttty == NULL) {
		printf ("vtload: unable to obtain vttty [%d]\n", vtmax);
		return;
	}

	PRINTV ("vtload: obtained vttty [%d] @%x\n", vtmax, vttty);

	/* determine which video adaptors are present */
	for (vtcount = 0, hw = vtHWtable ; hw != vtHWend ; hw ++) {
/* suppress board sensing since it seems to confuse some equipment */
#if 0
		if (! hwpresent (* hw))
			continue;
#endif

		/* remember our logical start */
		(* hw)->start = vtcount;
		PRINTV (", start %d\n", vtcount);

		/* allocate the necessary memory */
		for (i = 0; i < (* hw)->count; ++ i) {
			vp = vtdata [vtcount] = kalloc (sizeof (VTDATA));
			PRINTV ("     vtdata [%d] = @%x\n", vtcount, vp);
			if (vp == NULL || ! VTttyinit (vtcount)) {
				printf ("not enough memory for VTDATA\n");
				break;
			}

			/* fill in appropriately */
			* vp = const_vtdata;
			vp->vmm_port = (* hw)->port;
			vp->vmm_vseg = (* hw)->vidmemory.seg;
			vp->vmm_voff = (* hw)->vidmemory.off;

			vp->vt_ind = vtcount;
			vtdatainit (vp);
			if (i == 0) {
				vp->vmm_visible = VNKB_TRUE;
				vp->vmm_seg = vp->vmm_vseg;
				vp->vmm_off = vp->vmm_voff;
				vtupdscreen (vtcount);
			}
			(* hw)->found ++;
			vtcount ++;
		}
	}

	/*
	 * initialize vtconsole
	 */
	vtconsole = vtdata [vtactive = 0];
	vtconsole->vmm_invis = 0;		/* vtconsole cursor visible */

	/*
	 * Enable mmwatch () invocation every second.
	 */

	drvl [VT_MAJOR].d_time = 1;

	/*
	 * Initialize video display.
	 */
	for (i = 0 ; i < vtcount ; ++ i)
		vtmmstart (vttty [i]);

	fklength = 0;
}


/*
 * Unload entry point.
 */

void
isuload ()
{
	/* Restore pointers to original state. */
	vtconsole = vtdata [0];
	vtconsole->vmm_invis = 0;
	vtconsole->vmm_visible = VNKB_TRUE;

	if (vt_opened)
		printf ("VTclose with %d open screens\n", vt_opened);
	if (kbstate != KB_IDLE)
		printf ("kb: keyboard busy during unload\n");
}


/*
 * ship a single byte command to the keyboard
 */

void
kb_cmd (cmd)
unsigned cmd;
{
	int timeout;
	int s;

	s = sphi ();
	KBDEBUG2(" kb_cmd (%x)", cmd);
	while (kbstate != KB_IDLE)
		x_sleep (& kbstate, pritty, slpriSigCatch, "kb a");

	kbstate = KB_SINGLE;
	timeout = KBTIMEOUT;

	while (-- timeout > 0 && (inb (KBSTS_CMD) & STS_IBUF_FULL) != 0)
		/* DO NOTHING */ ;

	if (! timeout)
		printf ("kb: command timeout\n");
	else {
		outb (KBDATA, cmd);
		while (kbstate != KB_IDLE)
			x_sleep (& kbstate, pritty, slpriSigCatch, "kb b");
	}
	spl (s);
}


/*
 * ship a two byte command to the keyboard
 */

void
kb_cmd2(cmd, arg)
unsigned cmd, arg;
{
	int timeout;
	int s;

	s = sphi ();
	KBDEBUG3(" kb_cmd2(%x, %x)", cmd, arg);
	while (kbstate != KB_IDLE)
		x_sleep (& kbstate, pritty, slpriSigCatch, "kb c");

	kbstate = KB_DOUBLE_1;
	cmd2 = arg;
	prev_cmd = cmd;

	timeout = KBTIMEOUT;
	while (-- timeout > 0 && (inb (KBSTS_CMD) & STS_IBUF_FULL) != 0)
		/* DO NOTHING */ ;

	if (! timeout)
		printf ("kb: command timeout\n");
	else {
		outb (KBDATA, cmd);
		while (kbstate != KB_IDLE)
			x_sleep (& kbstate, pritty, slpriSigCatch, "kb d");
	}
	spl (s);
}


/*
 * update the keyboard status LEDS.
 * we chose the shift / lock key positions so this would be easy.
 * this flavor of routine is called while processing a system call on
 * behalf of the user.
 */

void
updleds ()
{
	if (! xlate)
		kb_cmd2 (K_LED_CMD, X11led);
	else
		kb_cmd2 (K_LED_CMD, (shift >> 1) & 0x7);
}


/*
 * same as above, but callable from interrupt routines and other places
 * which cannot sleep () waiting for the state machine to go idle.
 */

void
updleds2 ()
{
	int timeout;
	int s;

	timeout = KBTIMEOUT;
	s = sphi ();
	while (-- timeout > 0 && (inb (KBSTS_CMD) & STS_IBUF_FULL) != 0)
		/* DO NOTHING */ ;
	kbstate = KB_DOUBLE_1;
	if (! xlate)
		cmd2 = X11led;
	else
		cmd2 = (shift >> 1) & 0x7;

	prev_cmd = K_LED_CMD;
	outb (KBDATA, K_LED_CMD);
	spl (s);
}


/*
 * Open routine.
 */

#if	__USE_PROTO__
static void isopen(o_dev_t dev, int mode,
  int __NOTUSED(_flags), __cred_t * __NOTUSED(_credp))
#else
static void
isopen (dev, mode)
o_dev_t dev;
int mode;
#endif
{
	int s;
	TTY * tp;
	int	index = vtindex (dev);

	PRINTV ("isopen: %x\n", dev);
	if (! __IN_RANGE (0, index, vtcount - 1)) {
		set_user_error (ENXIO);
		return;
	}

	tp = vttty [index];
	if ((tp->t_flags & T_EXCL) != 0 && ! super ()) {
		set_user_error (ENODEV);
		return;
	}
	ttsetgrp (tp, dev, mode);

	s = sphi ();
	if (tp->t_open ++ == 0) {
		tp->t_flags = T_CARR;	/* indicate "carrier" */
		ttopen (tp);
	}
	spl (s);
#if 0
	updleds ();			/* update keyboard status LEDS */
#endif
}


void
vtdeactivate (vp_new, vp_old)
VTDATA	* vp_new, * vp_old;
{
	int i;
	VTDATA	* vpi;

	/*
	 * if changing to another screen on same video board
	 *	for all screens on same board as new screen
	 *		deactivate, but don't update
	 * else - changing to a screen on different board
	 *	for all screens NOT on same board as new screen
	 *		deactivate, but don't update
	 */

	if (vp_old->vmm_port != vp_new->vmm_port) {
		T_CON (8, printf ("deactivate %x->%x ",
		  vp_old->vmm_port, vp_new->vmm_port));

		for (i = 0; i < vtcount; ++ i) {
			vpi = vtdata [i];
			if (vpi->vmm_port != vp_new->vmm_port &&
			    vpi->vmm_invis == 0) {
				/* update, but don't deactivate */
				vpi->vmm_invis = -1; 
				vtupdscreen (i);
				vpi->vmm_invis = 0; 
			}
		}
	}
}


void
vtactivate (vp)
VTDATA * vp;
{
	VTDATA	* vpi;
	int	i;

	/* 
	 * copy from screen contents from heap segment to video memory 
	 * only if necessary
	 */

	/*
	 * If new active screen isn't visible, find currently visible
	 * screen and save to nonvideo memory, then restore new active
	 * screen from nonvideo.
	 */
	if (vp->vmm_visible == VNKB_FALSE) {
		for (i = 0 ; i < vtcount ; ++ i) {
			vpi = vtdata [i];

			if (vpi->vmm_port == vp->vmm_port
			  && vpi->vmm_visible == VNKB_TRUE) {
				ffcopy (vpi->vmm_voff, vpi->vmm_vseg,
					vpi->vmm_moff, vpi->vmm_mseg, TEXTBLOCK);
				break;
			}
		}
		ffcopy (vp->vmm_moff, vp->vmm_mseg,
			vp->vmm_voff, vp->vmm_vseg, TEXTBLOCK);
	}

	for (i = 0 ; i < vtcount ; ++ i) {
		vpi = vtdata [i];
		if (vpi->vmm_port == vp->vmm_port) {
			vpi->vmm_visible = VNKB_FALSE;
			vpi->vmm_seg = vpi->vmm_mseg;
			vpi->vmm_off = vpi->vmm_moff;
			if (vpi->vmm_seg == 0)
				printf ("[2]vpi->vmm_seg = 0\n");
			PRINTV ("vt.back seg %x off %x\n",
				vpi->vmm_seg, vpi->vmm_off);
		}		
	}

	/*
	 * Set new active terminal
	 */
	vp->vmm_visible = VNKB_TRUE;
	vp->vmm_seg = vp->vmm_vseg;
	vp->vmm_off = vp->vmm_voff;
	if (vp->vmm_seg == 0)
		printf ("vp->vmm_seg = 0\n");
}


/*
 * update the terminal to match vtactive
 */

void
updterminal (index)
int index;
{
	vtupdscreen (index);

	if (sep_shift)
		updleds2 ();
}


/*
 *
 * void
 * isvtswitch ()	-- deferred virtual terminal switch
 *
 *	Action: - save current shift key status
 *		- determine new active virtual terminal
 *		- deactivate shift key status of the current virtual terminal
 *		- deactivate current virtual terminal
 *		- activate shift key status of the new virtual terminal with 
 *		  the previously saved shift key status
 *		- activate new virtual terminal 
 *
 *	Notes:	isvtswitch () was scheduled as a deferred process by 
 *	process_key () which is a function called by vtnkbintr ().
 */

void
isvtswitch (key_val)
int key_val;
{
	int		new_index, i;
	unsigned	lockshift, nolockshift; 
	VTDATA		* vp = vtdata [vtactive];
	VTDATA		* vp_old, * vp_new;
	static int	vtprevious;

	/*
	 * lockshift status is maintained separately per session
	 * nolockshift status is meaningful only for the active session
	 */
	if (sep_shift) {
		/*
		 * shift, caps, and scroll lock are saved and restored
		 * when switching sessions.
		 */
		lockshift = shift & ((1 << scroll)|(1 << num)|(1 << caps));
		nolockshift = shift & ~((1 << scroll)|(1 << num)|(1 << caps));
	} else {
		/*
		 * shift state doesn't change when switching sessions.
		 */
		lockshift = 0;
		nolockshift = shift;
	}

	PRINTV ( "F%d: %d", key_val, vtactive );

#if 0
	if (key_val == VTKEY_HOME)
		new_index = 0;
	else if (key_val == VTKEY_NEXT) {
		new_index = vtactive;
		for (i = 0; i < vtcount; ++ i) {
			new_index = ++ new_index % vtcount;
			if (vttty [new_index]->t_open)
				break;
		}
	} else {
		new_index = vtindex (vtkey_to_dev (key_val));
		if (new_index < 0) {
			putchar ('\007');
			return;
		}
	}
#else

	switch (key_val) {
	case VTKEY_HOME:
		new_index = 0;
		break;

	case VTKEY_NEXT:
		new_index = vtactive;
		for ( i = 0; i < vtcount; ++ i ) {
			new_index = ++ new_index % vtcount;
			if ( vttty [new_index]->t_open )
				break;
		}
		break;

	case VTKEY_PREV:
		new_index = vtactive;
		for ( i = 0; i < vtcount; ++ i ) {
			new_index = (--new_index + vtcount) % vtcount;
			if ( vttty [new_index]->t_open )
				break;
		}
		break;

	case VTKEY_TOGL:
		new_index = vtprevious;
		break;

	default:
		new_index = vtindex (vtkey_to_dev (key_val));
		if ( new_index < 0) {
			putchar ('\a');
			return;
		}
	}
#endif
	T_CON (8, printf ("%d->%d ", vtactive, new_index));
	if (new_index == vtactive)
		return;

	/* Save which locking shift states are in effect. */

	vp_old = vtdata [vtactive];
	vp_new = vtdata [new_index];

	vp_old->vnkb_shift = lockshift;
	vtdeactivate (vp_new, vp_old);	/* deactivate old virtual terminal */

	/* Restore shift lock state, append current momentary shift state. */
	shift = vp_new->vnkb_shift | nolockshift;

	vtactivate (vp_new);		/* activate new virtual terminal */
	updterminal (new_index);
	vtprevious = vtactive;
	vtactive = new_index;		/* update vtactive */
}


/*
 * Close a tty.
 */
#if	__USE_PROTO__
static void isclose(o_dev_t dev,
  int __NOTUSED(_mode), int __NOTUSED(_flags), __cred_t * __NOTUSED(_credp))
#else
static void
isclose (dev)
o_dev_t dev;
#endif
{
	int s;
	TTY * tp = vttty [vtindex (dev)];

#if 0
	s = sphi ();
	if (-- tp->t_open == 0) {
		ttclose (tp);
		spl (s);
		if (index == vtactive)
			isvtswitch (VTKEY_HOME);
	} else
		spl (s);
#else
	if (-- tp->t_open == 0)
		ttclose (tp);
#endif
}


/*
 * Read routine.
 */

#if	__USE_PROTO__
static void isread(o_dev_t dev, IO * iop,
  __cred_t * __NOTUSED(_credp))
#else
static void
isread (dev, iop)
o_dev_t dev;
IO * iop;
#endif
{
	TTY * tp = vttty [vtindex (dev)];

	ttread (tp, iop, 0);
	if (tp->t_oq.cq_cc)
		vtmmtime (tp);
}


static TIM tp;

void
resetkb (action)
int action;
{
	if (action == 1) {
		timeout (& tp, 20, resetkb, 2);
		outb (KBCTRL, 0xCC);		/* Clock high */
	}

	if (action == 2) {
		int i = inb (KBDATA);
		outb (KBCTRL, 0xCC);		/* Clear keyboard */
		outb (KBCTRL, 0x4D);		/* Enable keyboard */
	}
}


/*
 * Set and receive the function keys.
 */

void
isfunction (c, v)
int c;
FNKEY * v;
{
	unsigned char * cp;
	unsigned i;
	unsigned char	numkeys = 0;

	if (c == TIOCGETF) {
		KBDEBUG (" TIOCGETF");
		if (! fk_loaded)
			set_user_error (EINVAL);
		else
			kucopy (fnkeys, v, fklength);	/* copy ours to user */
		return;
	}
	/* TIOCSETF */

	/*
	 * If we had a previous function key arena, free it up.
	 * Since we don't know how large the function key arena will
	 * be, we must size it in the user data space prior to
	 * (re)kalloc ()'ing it. This is ugly, but a helluva lot better
	 * than the old driver which used a hard coded limit of 150!
	 */

	KBDEBUG (" TIOCSETF");
	fk_loaded = 0;
	if (fnkeys != NULL)
		kfree (fnkeys);		/* free old arena */
	if (funkeyp != NULL)
		kfree (funkeyp);		/* free old ptr array */
	ukcopy (& v->k_nfkeys, & numkeys, sizeof (numkeys));

	/*
	 * I'd use offsetof (), but right now it is broken due to a
	 * compiler bug.
	 */

	fklength = sizeof (FNKEY);
	cp = (char *) (v + 1);
	for (i = 0; i < numkeys; i ++) {
		do {
			++ fklength;
		} while (getubd (cp ++) != DELIM);
	}

	fnkeys = (FNKEY *) kalloc (fklength);
	funkeyp = (unsigned char **) kalloc (numkeys * sizeof (char *));

	if (fnkeys == NULL || funkeyp == NULL) {
		if (fnkeys != NULL) {
			kfree (fnkeys);
			fnkeys = NULL;
		}

		if (funkeyp != NULL) {
			kfree (funkeyp);
			funkeyp = NULL;
		}

		set_user_error (ENOMEM);
		return;
	}

	cp = (char *) (fnkeys + 1);		/* point to Fn ... */
	v = (FNKEY *) (v + 1);			/* ... key arena */

	for (i = 0; i < numkeys; i ++) {
		funkeyp [i] = cp;	           /* save pointer */
		while ((* cp ++ = getubd (v ++)) != DELIM)  /* copy key */
			;
	}

	fnkeys->k_nfkeys = numkeys;
	fk_loaded = 1;
}


/*
 * Set the in-core keyboard mapping table.
 * The table is sorted by scan code prior to calling ioctl ().
 * All unused table entries (holes in the scan code map) have
 * a zero for the k_key field.
 * This makes key lookup at interrupt time fast by using the scan code
 * as an index into the table.
 */

void
issettable (vec)
char	* vec;
{
	unsigned i;
	int s;
	int timeout;
	static	KBTBL	this_key;	/* current key from kbd table */
	unsigned int cmd_byte;

	PRINTV (" TIOCSETKBT");
	kb_cmd2(K_SCANCODE_CMD, 3);		/* select set 3 */
	kb_cmd (K_ALL_TMB_CMD);			/* default: TMB for all keys */

	for (i = 0; i < MAX_KEYS; ++ i) {

		ukcopy (vec, & this_key, sizeof (this_key));
		kb [i] = this_key;		/* store away */

		vec += sizeof (this_key);
		if (this_key.k_key != i && this_key.k_key != 0) {
			printf ("kb: incorrect or unsorted table entry %d\n", i);
			set_user_error (EINVAL);
			return;
		}

		if (this_key.k_key != i)
			continue;		/* no key */

		switch (this_key.k_flags & TMODE) {

		case T:				/* typematic */
			kb_cmd2 (K_KEY_T_CMD, i);
			break;

		case M:				/* make only */
			kb_cmd2 (K_KEY_M_CMD, i);
			break;

		case MB:			/* make / break */
			kb_cmd2 (K_KEY_MB_CMD, i);
			break;

		case TMB:			/* typematic make / break */
			break;			/* this is the default */

		default:
			printf ("kb: bad key mode\n");
		}
	}

	updleds ();
	kb_cmd2 (K_SCANCODE_CMD, 3);		/* select set 3 */
	kb_cmd (K_ENABLE_CMD);			/* start scanning */

	/*
	 * The following code disables translation from the on-board
	 * keyboard / aux controller. Without disabling translation, the
	 * received scan codes still look like code set 1 codes even
	 * though we put the keyboard controller in scan code set 3.
	 * Yes, this is progress....
	 */

#if 0
	while (inb (KBSTS_CMD) & STS_IBUF_FULL)
		;
	outb (KBSTS_CMD, C_READ_CMD);		/* read controller cmd byte */
	while (!(inb (KBSTS_CMD) & STS_OBUF_FULL))
		;
	cmd_byte = inb (KBDATA);
	KBDEBUG2(" cmd_byte =%x", cmd_byte);
#endif

	timeout = KBTIMEOUT;
	s = sphi ();
	while ((inb (KBSTS_CMD) & STS_IBUF_FULL) != 0 && -- timeout > 0)
		/* DO NOTHING */ ;

	outb (KBSTS_CMD, C_WRITE_CMD);		/* write controller cmd byte */

	for (timeout = 50 ; -- timeout > 0;)
		/* DO NOTHING */ ;

	timeout = KBTIMEOUT;
	while ((inb (KBSTS_CMD) & STS_IBUF_FULL) != 0 && -- timeout > 0)
		/* DO NOTHING */ ;

	outb (KBDATA, KBCMDBYTE);		 /* turn off translation */	

	timeout = KBTIMEOUT;
	while ((inb (KBSTS_CMD) & STS_IBUF_FULL) != 0 && -- timeout > 0)
		/* DO NOTHING */ ;
	spl (s);

#if DEBUG || 1
	kb_cmd2 (K_SCANCODE_CMD, 0);		/* query s.c. mode */
#endif
	++ table_loaded;
	PRINTV ("... TIOCSETKBT\n");
}


/*
 * Get the in-core keyboard mapping table and pass it to the user.
 */

void
isgettable (vec)
char	* vec;
{
	KBDEBUG (" TIOCGETKBT");
	kucopy (kb, vec, sizeof (kb));
}


/*
 * Ioctl routine.
 * nb: archaic TIOCSHIFT and TIOCCSHIFT no longer needed / supported.
 */

#if	__USE_PROTO__
static void isioctl(o_dev_t dev, int com, __VOID__ * vec,
  int __NOTUSED (_mode), __cred_t * __NOTUSED( _credp), int *__NOTUSED(_rvalp))
#else
static void
isioctl (dev, com, vec)
o_dev_t dev;
int com;
__VOID__ * vec;
#endif
{
	int s;

	switch (com) {
#define KDDEBUG 0
#if KDDEBUG
	case KDMEMDISP: {
		struct kd_memloc * mem;
		unsigned char ub, pb;

		mem = (struct kd_memloc *)vec;
		pxcopy (mem->physaddr, & pb, 1, SEL_386_KD);
		ub = getubd (mem->vaddr);
		printf ("User's byte %x (%x), Physical byte %x, Addresses %x %x\n",
			mem->ioflg, ub, pb, mem->vaddr, mem->physaddr);
		break;
	}
#endif
	case KDMAPDISP: {
		struct kd_memloc * mem;
		mem = (struct kd_memloc *) vec;
		mapPhysUser (mem->vaddr, mem->physaddr, mem->length);
	}

	case KDDISABIO:
		__kddisabio();
		break;

	case KDENABIO:
		__kdenabio();
		break;

	case KDADDIO:
		__kdaddio((unsigned short)vec);
		break;

	case KDDELIO:
		__kddelio((unsigned short)vec);
		break;

	case KIOCSOUND: {
		if ((int) vec) {
			outb (TIMER_CTL, 0xB6); 
			outb (TIMER_CNT, (int) vec & 0xFF);
			outb (TIMER_CNT, (int) vec >> 8);
			/* Turn speaker on */
			outb (SPEAKER_CTL, inb (SPEAKER_CTL) | 3);
		} else 
			outb (SPEAKER_CTL, inb (SPEAKER_CTL) & ~ 3);
		break;
	}

	case KDGKBSTATE: 
		* (char *) vec = 0;
		if (shift & ((1 << lshift) | ( 1 << rshift)))
			* (char *) vec |= M_KDGKBSTATE_SHIFT;
		if (shift & ((1 << lctrl) | (1 << rctrl)))
			* (char *) vec |= M_KDGKBSTATE_CTRL;
		if (shift & ((1 << lalt) | (1 << ralt)))
			* (char *) vec |= M_KDGKBSTATE_ALT;
		break;

	case KDSKBMODE: {
		static int vtB4X11;

#if	0
		outb (KBCTRL, 0x0C);		   /* Clock low */
		timeout (& tp, 3, resetkb, 1);	   /* wait about 20-30ms */
#endif
		if (xlate > (int) vec) {	/* Turning translation off */
			kb_cmd2 (K_SCANCODE_CMD, 1);	 /* set 1 for X */

#if 0
			/* deactivate virtual terminal */
			vtB4X11 = vtactive;
			vtdeactivate (vtdata [vtactive], vtdata [vtcount]);
			vtactivate (vtdata [vtcount]);
			vtactive = vtcount;
#endif
		} else if (xlate < (int) vec) {	/* turning translation on */
			kb_cmd2(K_SCANCODE_CMD, 3);	 /* set 3 for COH */
#if 0
			/* reactivate virtual terminal */
			vtactivate (vtdata [vtB4X11]);
			vtactive = vtB4X11;
#endif
		}
		xlate = (int) vec;
#if	0
		kb_cmd (K_ALL_TMB_CMD);	/* default: TMB for all keys */
#endif
		break;
	}
     
	case KDSETLED: {
		X11led = (int) vec;
		updleds ();
		break;		  
	}

	case KDGETLED:
		* (char *) vec = 0;
		if (shift & (1 << num))
			* (char *) vec |= M_KDGETLED_NUMLOCK;
		if (shift & (1 << caps))
			* (char *) vec |= M_KDGETLED_CAPLOCK;
		if (shift & (1 << scroll))
			* (char *) vec |= M_KDGETLED_SCRLOCK;
		break;

	case TIOCSETF:
	case TIOCGETF:
		isfunction (com, (char *) vec);
		break;

	case TIOCSETKBT:
		issettable (vec);
		break;

	case TIOCGETKBT:
		isgettable (vec);
		break;

	case KIOCINFO:
		* (int *) vec = 0x6664;
		break;

	default:				/* pass to TTY driver */
		s = sphi ();
		ttioctl (vttty [vtindex (dev)], com, vec);
		spl (s);
		break;
	}
}


/*
 * Poll routine.
 */

#if	__USE_PROTO__
static int ispoll(o_dev_t dev, int ev, int msec)
#else
static int
ispoll (dev, ev, msec)
o_dev_t dev;
int ev;
int msec;
#endif
{
	return ttpoll (vttty [vtindex (dev)], ev, msec);
}


/**
 *
 * void
 * isin (c)	-- append character to raw input silo
 * char c;
 */

static void
isin (c)
int c;
{
	int cache_it = 1;
	TTY * tp = vttty [vtactive];
	void ttstart ();

	/*
	 * If using software incoming flow control, process and
	 * discard t_stopc and t_startc.
	 */
	if (_IS_IXON_MODE (tp)) {
		if (_IS_START_CHAR (tp, c) ||
		    (_IS_IXANY_MODE (tp) && (tp->t_flags & T_STOP) != 0)) {
			tp->t_flags &= ~ (T_STOP | T_XSTOP);
			ttstart (tp);
			cache_it = 0;
		} else if (_IS_STOP_CHAR (tp, c)) {
			if ((tp->t_flags & T_STOP) == 0)
				tp->t_flags |= (T_STOP | T_XSTOP);
			cache_it = 0;
		}
	}

	/*
	 * Cache received character.
	 */

	if (cache_it) {
		in_silo.si_buf [in_silo.si_ix] = c;

		if (++ in_silo.si_ix >= sizeof (in_silo.si_buf))
			in_silo.si_ix = 0;
	}
}


/*
 * Process a key given its scan code and direction.
 * 
 * In this table driven version of the keyboard driver, we trade off the
 * code complexity associated with all the black magic that used to be
 * performed on a per-key basis with the increased memory requirements
 * associated with the table driven approach.
 */

#if SWANFIX
void
process_key (key, up, e0esc)
unsigned key;
char	 up, e0esc;
#else
void
process_key (key, up)
unsigned key;
int	 up;
#endif
{
	unsigned char * cp;
	KBTBL	key_vals;			/* table values for this key */
	unsigned val;
	unsigned char flags;
	TTY * tp = vttty [vtactive];
	VTDATA * vp = vtdata [vtactive];

	KBDEBUG3(" proc (%x %s)", key, (up ? "up" : "down"));
	if (! table_loaded)
		return;				/* throw away key */

	/*
	 *  It's ugly but, if e0esc, then we use the ALT_GR field to point
	 *  at the actual table entry we want.  We weren't really using the
	 *  ALT_GR field anyway.  Trouble remapping shift keys because
	 *  loader requires all entries to be identical, thus ALT_GR is
	 *  by default being used.
	 */

#if SWANFIX
	if (VTSWAN && e0esc && (kb [key].k_flags & S) == 0)
		key = kb [key].k_val [ALT_GR];   /* Ugly kludge */
#endif
	key_vals = kb [key];

	if (key_vals.k_key != key)		/* empty entry */
		return;
	flags = key_vals.k_flags;

	if (flags & S) {			/* some shift / lock key ? */
		switch (key_vals.k_val [BASE]) {
		case caps:
		case num:
			if (! up) {
				shift ^= (1 << key_vals.k_val [BASE]);
				updleds2 ();
			}
			break;

		case scroll:
			if (! up) {
				shift ^= (1 << key_vals.k_val [BASE]);
				updleds2 ();
				if (_IS_RAW_INPUT_MODE (tp)) {
					if (tp->t_flags & T_STOP)
						isin (tp->t_tchars.t_startc);
					else
						isin (tp->t_tchars.t_stopc);
				}
			}
			break;

		default:
			if (up)
				shift &= ~ (1 << key_vals.k_val [BASE]);
			else
				shift |= (1 << key_vals.k_val [BASE]);
			break;
		}

		/*
		 * Calculate the shift index based upon the state of
		 * the shift and lock keys.
		 */

		sh_index = BASE;		/* default condition */
		if (shift & (1 << altgr))
			sh_index = ALT_GR;
		else {
			if (shift & ((1 << lalt) | (1 << ralt)))
				sh_index |= ALT;
			if (shift & ((1 << lctrl) | (1 << rctrl)))
				sh_index |= CTRL;
			if (shift & ((1 << lshift) | (1 << rshift)))
				sh_index |= SHIFT;
		}

		T_CON (2, printf ("shift =%x sh_index =%d\n", shift, sh_index));
		return;
	} /* if (flags & S) */

	/*
	 * If the key has no value in the current
	 * shift state, the key is just tossed away.
	 */

	if (up || key_vals.k_val [sh_index] == none)
		return;

	if (((flags & C) != 0 && (shift & (1 << caps)) != 0) ||
	    ((flags & N) != 0 && (shift & (1 << num))) != 0)
		val = key_vals.k_val [sh_index ^ SHIFT];
	else
		val = key_vals.k_val [sh_index];

	/*
	 * Check for function key or special key implemented as
	 * a function key (reboot == f0, tab and back-tab, etc).
	 */

	if (flags & F) {
		PRINTV ("<{F%d}>", val);
		if (VTKEY (val)) {
			T_CON (4,
			  printf ( "<{F%d !!}>\b\b\b\b\b\b\b\b\b\b", val));
			defer (isvtswitch, val);
			return;
		}
		/* If the tty is not open, ignore it */
		if (! tp->t_open)
			return;
#if GREEKFIX
		if (VTGREEK && val == fgk) {
			ToggleGreek ();
			return;
		}
#endif /* GREEKFIX */

#if	0
		if (val == 0 && ! up && KBBOOT)
			boot ();
#endif
		if (! fk_loaded || val >= fnkeys->k_nfkeys)
			return;
		if ((cp = funkeyp [val]) == NULL) /* has a value? */
			return;
		while (* cp != DELIM)
			isin (* cp ++);		/* queue up Fn key value */
		return;
	}

	/*
	 * Normal key processing.
	 */
	/* If the tty is not open, ignore it */
#if GREEKFIX
	if (tp->t_open)
		if (VTGREEK) {
			if (ToGreek (& val))
				isin (val);
		} else
			isin (val);
#else
	if (tp->t_open)
		isin (val);		 /* send the char */
#endif /* GREEKFIX */
}


/**
 *
 * void
 * isbatch ()	-- raw input conversion routine
 *
 *	Action:	Enable the video display.
 *		Canonize the raw input silo.
 *
 *	Notes:	isbatch () was scheduled as a deferred process by vtnbkintr ().
 */

static void
isbatch (tp)
TTY * tp;
{
	int c;
	static int lastc;
	VTDATA * vp = (VTDATA *) tp->t_ddp;

	/*
	 * Ensure video display is enabled.
	 */

	if (vp->vmm_visible)
		vtmm_von (vp);

	isbusy = 0;

	/*
	 * Process all cached characters.
	 */

	while (in_silo.si_ix != in_silo.si_ox) {
		/*
		 * Get next cached char.
		 */
		c = in_silo.si_buf [in_silo.si_ox];

		if (in_silo.si_ox >= sizeof (in_silo.si_buf) - 1)
			in_silo.si_ox = 0;
		else
			in_silo.si_ox ++;

		if (islock == 0 || _IS_INTERRUPT_CHAR (tp, c) ||
		    _IS_QUIT_CHAR (tp, c)) {
			ttin (tp, c);
		} else if ((c == 'b') && lastc == ESCAPE_CHAR) {
			islock = 0;
			ttin (tp, lastc);
			ttin (tp, c);
		} else if (c == 'c' && lastc == ESCAPE_CHAR) {
			ttin (tp, lastc);
			ttin (tp, c);
		} else
			putchar ('\a');
		lastc = c;
	}
}


/*
 * Receive interrupt.
 */

#define	K_E0ESC	0xE0		/* Swan Keyboard, Strange Escape Byte	*/

void
vtnkbintr ()
{
	register unsigned c;
	register unsigned r;
	static	char keyup;
#if SWANFIX
	static	char e0esc;
#endif

	/*
	 * Schedule raw input handler if not already active.
	 */
	if (! isbusy) {
		defer (isbatch, vttty [vtactive]);
		isbusy = 1;
	}

	/*
	 * Pull character from the data
	 * port. Pulse the KBFLAG in the control
	 * port to reset the data buffer.
	 */

	r = inb (KBDATA) & 0xFF;
	c = inb (KBCTRL);
	outb (KBCTRL, c | KBFLAG);
	outb (KBCTRL, c);

	/*
	 * check returned value from keyboard to see if it's a command
	 * or status back to us. If not, it we assume that it's a key code.
	 */

	KBDEBUG2 ("\nintr (%x) ", r);
	if (! xlate) switch (r) {
	case K_BAT_BAD:
		printf ("kb: keyboard BAT failed\n");
		break;

	case K_RESEND:
		KBDEBUG ("\nkb: request to resend command\n");
		outb (KBDATA, prev_cmd);
		break;

	case K_OVERRUN_23:
		printf ("kb: keyboard buffer overrun\n");
		break;

	case K_ACK:
		/*
		 * we received an ACKnowledgement from the keyboard.
		 * advance the state machine and continue.
		 */

		KBDEBUG (" ACK ");
		switch (kbstate) {
		case KB_IDLE:			/* shouldn't happen */
#if 0
 			printf ("vtnkb: ACK while idle\n");
#endif
			break;

		case KB_SINGLE:			/* done with 1-byte command */
		case KB_DOUBLE_2:		/* done w / 2nd of 2-byte cmd */
			kbstate = KB_IDLE;
			wakeup (& kbstate);
			break;

		case KB_DOUBLE_1:
			kbstate = KB_DOUBLE_2;
			outb (KBDATA, cmd2);
			break;

		default:
			printf ("kb: bad kbstate %d\n", kbstate);
			break;
		}
		break;

	default:
         	isin (r);
             	break;
	} else switch (r) {

	case K_BREAK:
		keyup = 1;			/* key going up */
		break;

	case K_ECHO_R:
	case K_BAT_OK:
		break;				/* very nice, but ignored */

	case K_BAT_BAD:
		printf ("kb: keyboard BAT failed\n");
		break;

	case K_RESEND:
		KBDEBUG ("\nkb: request to resend command\n");
		outb (KBDATA, prev_cmd);
		break;

	case K_OVERRUN_23:
		printf ("kb: keyboard buffer overrun\n");
		break;

	case K_ACK:
		/*
		 * we received an ACKnowledgement from the keyboard.
		 * advance the state machine and continue.
		 */
		KBDEBUG (" ACK ");
		switch (kbstate) {
		case KB_IDLE:			/* shouldn't happen */
#if 0
			printf ("vtnkb: ACK while idle ");
#endif
			break;

		case KB_SINGLE:			/* done with 1-byte command */
		case KB_DOUBLE_2:		/* done w / 2nd of 2-byte cmd */
			kbstate = KB_IDLE;
			wakeup (& kbstate);
			break;

		case KB_DOUBLE_1:
			kbstate = KB_DOUBLE_2;
			outb (KBDATA, cmd2);
			break;

		default:
			printf ("kb: bad kbstate %d\n", kbstate);
			break;
		}
		break;
#if SWANFIX
	case K_E0ESC:
		if (VTSWAN) {
			e0esc = 1;
			break;
		}
#endif
	default:
#if SWANFIX
		process_key (r, keyup, e0esc);
		e0esc = 0;
#else
		process_key (r, keyup);
#endif
		keyup = 0;
	}
}


/**
 *
 * void
 * ismmfunc (c)	-- process keyboard related output escape sequences
 * char c;
 */

void
ismmfunc (c)
int c;
{
	switch (c) {
	case 't':	/* Enter numlock */
		shift |= (1 << num);
		updleds ();			/* update LED status */
		break;

	case 'u':	/* Leave numlock */
		shift &= ~ (1 << num);
		updleds ();			/* update LED status */
		break;

	case '=':			/* Enter alternate keypad -- ignored */
	case '>':			/* Exit alternate keypad -- ignored */
		break;

	case 'c':	/* Reset terminal */
		islock = 0;
		break;
	}
}


/*
 * unlock the scroll in case an interrupt character is received
 */

void
kbunscroll ()
{
	shift &= ~ (1 << scroll);
	updleds ();
}


/*
==============================================================================
==============================================================================
*/

int
VTttyinit (i)
int i;
{
	TTY * tp;

	/*
	 * get pointer to TTY structure from kernal memory space
	 */
	if ((tp = vttty [i] = (TTY *)kalloc (sizeof (TTY))) == NULL)
		return 0;
	PRINTV ("     vttty [%d]: @%x, ", i, (unsigned long)tp);

	tp->t_param = NULL;
	tp->t_start = & vtmmstart;

	tp->t_ddp = (char *) vtdata [i];
	PRINTV ("data @%lx\n", (unsigned long)tp->t_ddp);
	return 1;
}


/*
 * Given device number, return index for vtdata [], vttty [], etc.
 *
 * Major number must be VT_MAJOR for CPU to get here.
 *
 *      Minor Number	Index Value
 *	----- ------ 	----- -----  
 *	0000  0000	vtactive ... device (2, 0) is the active screen
 *	0000  0001	0
 *	0000  0010	1
 *	0000  0011	2
 *	   ....
 *	0000  1111	14
 *
 *	0100  xxxx	xxxx ... color devices only
 *	0101  xxxx	xxxx - (# of color devices found) ... monochrome only
 *
 * Return value is in range 0 to vtcount-1 for valid minor numbers,
 * -1 for invalid minor numbers.
 */

int
vtindex (dev)
dev_t dev;
{
	int	ret = -1;

	if (dev & VT_PHYSICAL) {
		int	hw = (dev >> 4) & 3;
		int	hw_index = dev & 0x0F;

		if (hw_index < vtHWtable [hw]->found)
			ret = vtHWtable [hw]->start + hw_index;
	} else {
		int	lg_index = dev & 0x0F;

		if (lg_index == 0)
			ret = vtactive;
		if (lg_index > 0 && lg_index <= vtcount ) 
			ret = lg_index-1;
	}
	if (ret >= 0)
		ret %= vtcount;
	else
		PRINTV ( "vtindex: (%x) %d. invalid !\n", dev, ret );
	return ret;
}


/*
 * Given a function key number (e.g. vt0),
 * return the corresponding minor device number.
 *
 * Assume valid key number (VTKEY (fnum) is true) by the time we get here.
 */

int
vtkey_to_dev (fnum)
int fnum;
{
	if (fnum >= vt0 && fnum <= vt15)
		return fnum - vt0 + 1;

	if (fnum >= color0 && fnum <= color15)
		return (fnum - color0) | (VT_PHYSICAL | VT_HW_COLOR);
	if (fnum >= mono0 && fnum <= mono15)
		return (fnum - mono0) | (VT_PHYSICAL | VT_HW_MONO);

	printf ("vtkey_to_dev (%d)! ", fnum);
	return 0;
}


#if GREEKFIX
/*
 * ToggleGreek () must be called every time Alt + Enter is pressed.
 * It toggles the "InGreek" flag and resets all others.
 *
 * ToGreek (unsigned *) returns FALSE if val is a dead key (Greek
 * accent key) that must NOT be processed, TRUE otherwise.
*/	

static int	InGreek = 0;
static int	Tonos = 0;
static int	Dialytika = 0;

static int	UpperG [26] = {
  128, 129, 150, 131, 132, 148, 130, 134, 136, 141, 137, 138, 139,
  140, 142, 143, 58, 144, 145, 146, 135, 151, 145, 149, 147, 133};

static int	LowerG [26] = {
  152, 153, 175, 155, 156, 173, 154, 158, 160, 165, 161, 162, 163,
  164, 166, 167, 59, 168, 169, 171, 159, 224, 170, 174, 172, 157};

static int	VoyelG [7] = {152, 156, 158, 160, 166, 172, 224};
static int	TonedG [7] = {225, 226, 227, 229, 230, 231, 233};

void
ToggleGreek ()
{
	InGreek 	^= 1;
	Tonos		 = 0;
	Dialytika	 = 0;
}

int
ToGreek (ip)
unsigned	* ip;
{
	unsigned	i, j;

	i = * ip;

	/* If Not Greek exit */
	if (! InGreek)
		return 1;

	/* Capture dead keys */
	if (i == ';') {
		Tonos ^= 1;
		return 0;
	}	

	if (i == ':') {
		Dialytika ^= 1;
		return 0;
	}	

	/* Check if character translation needed */
	if (i >= 'A' && i <= 'Z')
		i = UpperG [i - 'A'];
	else if	(i >= 'a' && i <= 'z')
		i = LowerG [i - 'a'];
	else
		return 1;

	/* Check if any accent has to be added */
	if (Tonos) {
		Tonos = 0;
		for (j = 0;j < 7;j ++)
		if (i == VoyelG [j]) {
			i = TonedG [j];
			break;
		}
	} else if (Dialytika) {
		Dialytika = 0;
		if (i == 160)
			i = 228;
		else if (i == 172)
			i = 232;
	}

	/* Exit point for translated characters	*/
	* ip = i;
	return 1;
}

#endif /* GREEKFIX */


/*
 * Configuration table.
 */

CON vtnkbcon ={
	DFCHR | DFPOL,			/* Flags */
	KB_MAJOR,			/* Major index */
	isopen,				/* Open */
	isclose,			/* Close */
	NULL,				/* Block */
	isread,				/* Read */
	vtmmwrite,			/* Write */
	isioctl,			/* Ioctl */
	NULL,				/* Powerfail */
	vtmmwatch,			/* Timeout */
	isload,				/* Load */
	isuload,			/* Unload */
	ispoll				/* Poll */
};