/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:cons.c 12.0$ */
/* $ACIS:cons.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/cacons/RCS/cons.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:cons.c 12.0$";
#endif

/*
 * IBM RT PC Workstation console driver
 */
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/systm.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../h/kernel.h"
#include "../machine/debug.h"

#include "../machinecons/screen_conf.h"
#include "../machinecons/consdefs.h"
#include "../machinecons/consvars.h"
#include "../machine/scr.h"	/* for CCR bits */
#include "../machinecons/kls.h"
#include "../machine/io.h"

#ifdef ATR
#include "../ca_atr/pcif.h"
#endif ATR


int edebug = 0;
extern	char	*panicstr;
int NSCREEN;

#define ISPEED B9600

int	cnstart();
int	ttrstrt();

cnatch (dev)
register dev_t dev;
{
	register struct tty *tp;
	register SCREEN_INFO *si;
EDEBUG (0x01, aed_pr("in cnatch\n"));

	/* Initialize all the screens on the system */
	screen_init(cons, cons_info);

	/* Get the first found screen to focus input/output to at first */
	tp = &cons[CONS_GEN];
	si = &cons_info[cons_display];

	tp->t_state = 0;
	ttychars(tp);
	tp->t_ospeed = tp->t_ispeed = ISPEED;
	tp->t_flags = EVENP|ECHO|CRMOD;

	/* Have to set up the screens and emulator for kernel messages */
	sichars (cons_display, CE_DEFAULT, si);
	(*emulsw[si->oute.emulator].e_open)(cons_display, tp, si);

	/* Make sure klsinit is done by open */
	klsinit(); 

	/* Set the default hot key to switch the input focus from consoles */
	cons_switch_focus_code = CONS_SWITCH_FOCUS_CODE;
}

/*ARGSUSED*/
cnopen(dev, flag)
dev_t dev;
{
	register int tty_unit,scr_unit,emul_unit;
	register SCREEN_INFO *si,*sii;
	register struct tty *tp;
	int i;
	int error = 0;
	int minor_open = 0;

	/*
	 * This allows both andrew and X to work when only using one 
	 * console.Basically, if there is no CONSDEV_TTY's open, and
	 * there are no graphics devices with nonstandard keyboard 
	 * emulators open, the input focus must point to /dev/console.
	 */
	for (i=0;i < NSCREEN ; i++) {
		if (screen_sw[i].flags & CONSDEV_TTY)
			break;
		if ((screen_sw[i].flags & CONSDEV_GRA) &&
			(cons_info[i].ine.emulator != E_KBDINPUT))
			break;
	}
	if (i >= NSCREEN) {
		cons_if = CONS_GEN;
	}
	/*
	 * if we open /dev/console, we need to get focus dependend info
	 * note that /dev/console reads from CONS_GEN and writes to
	 * cons_display. Keyboard input only goes to CONS_GEN in the
	 * above case.
	 */
	if (minor(dev) == CONS_GEN) {
		scr_unit = cons_display;
		tty_unit = CONS_GEN;
		/*
		 * console output is being forwarded to a tty device
		 * no further open work is necessary.
		 */
		if (cons_tp)
			return(0);
		if (cons[scr_unit].t_state & TS_ISOPEN)
			return(0);
	} else {
		minor_open = tty_unit = scr_unit = MINOR_SCREEN (dev);
	}
	if (scr_unit >= NSCREEN)
		return (ENXIO);
		
	if ((screen_sw[scr_unit].flags & CONSDEV_USEIT) != CONSDEV_USEIT)
		return (ENXIO);

	si = &cons_info[scr_unit];
	sii = &cons_info[tty_unit];
	tp = &cons[tty_unit];

	emul_unit = MINOR_EMUL(dev);

	tp->t_oproc = cnstart;
	if ((tp->t_state&TS_ISOPEN) == 0) {
		ttychars(tp);
		tp->t_ospeed = tp->t_ispeed = ISPEED;
		tp->t_state = TS_CARR_ON;
		tp->t_flags = EVENP | ECHO | CRMOD;
		if (!_init_kbd)
			klsinit();
		sichars (scr_unit, emul_unit, si);
		cons_emul_flag[scr_unit] = emul_unit;
		/*
		 * set the input emulator for CONS_GEN
		 */
		if (tty_unit == CONS_GEN) {
			sichars (scr_unit, CE_DEFAULT, sii);
			cons_emul_flag[tty_unit] = CE_DEFAULT;
		}

	}
	if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
		return (EBUSY);

	if (minor_open) {
		screen_sw[scr_unit].flags |= 
			(emul_unit ? CONSDEV_GRA:CONSDEV_TTY);
		if ((!emul_unit) && cons_if == CONS_GEN)
			cons_if = cons_display;
	}
	
	if (MINOR_BUS(dev))
		set_ccr(CCR_SMP|CCR_IMP);	/* allow bus access */

	if (emul_unit && (cons_emul_flag[scr_unit] != emul_unit)) {
		sichars (scr_unit, emul_unit, si);
		cons_emul_flag[scr_unit] = emul_unit;
	}

	if (error = (*linesw[tp->t_line].l_open)(tty_unit, tp))
		return (error);

	EDEBUG (0x04, printf("CNOPEN: call ttyopen (%x)\n",
		emulsw[si->oute.emulator].e_open));

	(*emulsw[si->oute.emulator].e_open)(scr_unit, tp, si);

	return ((*emulsw[sii->ine.emulator].e_open)(tty_unit, tp, sii));
}

/*ARGSUSED*/
cnclose(dev)
dev_t dev;
{
	register struct tty *tp;
	register SCREEN_INFO *si,*sii;
	register int tty_unit,scr_unit;
	int emul_unit;
	int i;

	DEBUGF (cndebug & 0x01, printf ("In CNCLOSE(%x)\n",dev));

	if (minor(dev) == CONS_GEN) {
		tty_unit = CONS_GEN;
		scr_unit = cons_display;
		if (cons_tp)
			return(0);
	} else {
		tty_unit = scr_unit = MINOR_SCREEN (dev);
	}
	si = &cons_info[scr_unit];
	sii = &cons_info[tty_unit];
	tp = &cons[tty_unit];
	if ((tp->t_state&TS_ISOPEN)==0)
		return(0);

	/* If a process has this screen open as a minor emulator device
	 * don't close it until he says so.
	 */
	if (cons_emul_flag[scr_unit] != (emul_unit = MINOR_EMUL(dev))) {
		DEBUGF (cndebug & 0x01, printf ("CNCLOSE: emulate change.\n"));
		if (tty_unit == CONS_GEN)
			return(0);
		screen_sw[scr_unit].flags &= (emul_unit ? ~CONSDEV_GRA:~CONSDEV_TTY);
		return (0);
	}

	/* Close the Emulators */
	(*emulsw[si->oute.emulator].e_close)(tp, si);

	(*emulsw[sii->ine.emulator].e_close)(tp, sii);

	/* Reset privalages for User just in case */
	screen_sw[scr_unit].flags |= CONSDEV_USER;

	/* If this was a symbolic console then reset to default emulators */
	if (emul_unit) {
		/* Reset the Screen */
		DEBUGF (cndebug & 0x01, printf ("CNCLOSE: close gra.\n"));
		cons_emul_flag[scr_unit] = CE_DEFAULT;
		sichars (scr_unit, CE_DEFAULT, si);
		(*screen_sw[scr_unit].init)();
		screen_sw[scr_unit].flags |= CONSDEV_INIT;

		/* Open default emulators */
		(*emulsw[si->oute.emulator].e_open)(scr_unit, tp, si);
		(*emulsw[si->ine.emulator].e_open)(scr_unit, tp, si);

		/* Dump anything put into the console buffer */
		buf_dump_console(scr_unit);

		/* Since This was a symbolic/emulator console doesn't shut
		 * down the console just return after changing back to 
		 * standard emulators.
		 */
		screen_sw[scr_unit].flags &= ~CONSDEV_GRA;
		if ((screen_sw[scr_unit].flags & CONSDEV_TTY) == 0) {
			(*emulsw[si->oute.emulator].e_close)(tp, si);
			(*emulsw[si->ine.emulator].e_close)(tp, si);
		} else {
			return(0);
		}
	}

	/* Close the normal line discipline */
	(*linesw[tp->t_line].l_close)(tp);

	if (tty_unit != CONS_GEN)
		screen_sw[scr_unit].flags &= ~CONSDEV_TTY;

	for (i=0;i < NSCREEN ; i++) {
		if (screen_sw[i].flags & CONSDEV_TTY)
			break;
		if ((screen_sw[i].flags & CONSDEV_GRA) &&
			(cons_info[i].ine.emulator != E_KBDINPUT))
			break;
	}
	if (i >= NSCREEN) {
		cons_if = CONS_GEN;
	}
	return (ttyclose(tp));
}

/*ARGSUSED*/
cnread(dev, uio)
dev_t dev;
register struct uio *uio;
{
	register int tty_unit;
	register struct tty *tp;
	register SCREEN_INFO *sii;

	tty_unit = MINOR_SCREEN (dev);
	sii = &cons_info[tty_unit];
	tp = &cons[tty_unit];

	DEBUGF (cndebug & 0x01, printf ("In CNREAD(%x,%x)\n",dev,uio));
	return ((*emulsw[sii->ine.emulator].e_read)(tp, uio, sii));
}

/*ARGSUSED*/
cnwrite(dev, uio)
register dev_t dev;
register struct uio *uio;
{
	register int scr_unit,tty_unit;
	register struct tty *tp;
	register SCREEN_INFO *si;
	register int emul_unit;

	DEBUGF (cndebug & 0x01, printf ("In CNWRITE(%x,%x)\n",dev,uio));

	if (minor(dev) == CONS_GEN) {
		scr_unit = cons_display;
		tty_unit = CONS_GEN;
		if (cons_tp)
			return((*linesw[cons_tp->t_line].l_write)(cons_tp,uio));
	} else {
		tty_unit = scr_unit = MINOR_SCREEN (dev);
	}
	si = &cons_info[scr_unit];
	tp = &cons[tty_unit];

	/* If nonstandard device open and a write occurs from the standard
	   device then write to buf emulator.
	 */
	emul_unit = MINOR_EMUL(dev);
	if (!emul_unit && (cons_emul_flag[scr_unit] != emul_unit))
		return (buf_write(tp, uio, si));
	else
		return ((*emulsw[si->oute.emulator].e_write)(tp, uio, si));

}

/*ARGSUSED*/
cnioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
	register int tty_unit,scr_unit;
	register struct tty *tp;
	register SCREEN_INFO *si,*sii;
	register int error;

	if (minor(dev) == CONS_GEN) {
		scr_unit = cons_display;
	} else {
		scr_unit = MINOR_SCREEN (dev);
	}
	tty_unit = MINOR_SCREEN(dev);
	si = &cons_info[scr_unit];
	sii = &cons_info[tty_unit];
	tp = &cons[tty_unit];

	error = (*emulsw[sii->ine.emulator].e_ioctl)(tp, cmd, addr, sii);
	if (error >= 0)
		return (error);

	error = (*emulsw[si->oute.emulator].e_ioctl)(tp, cmd, addr, si);
	if (error >= 0)
		return (error);

	/* for input first */
	error = siioctl(tp, cmd, addr, sii);
	if (error >= 0)
		return (error);
	/* then for output */
	error = siioctl(tp, cmd, addr, si);
	if (error >= 0)
		return (error);

	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr);
	if (error >= 0)
		return (error);
	error = ttioctl(tp, cmd, addr, flag);
	if (error > 0) {
		if (cmd == TIOCSETP || cmd == TIOCSETN) {
			if (!_init_kbd)
				klsinit();
		}
		return (error);
	}
	if (error < 0)
		error = ENOTTY;
	return (error);
}

/* This routine is called from a line discipline routine when a user does
 * a write or on input interrupt to echo a character.  In both cases it should
 * and does come in a protected spl5 priority.  We will lower the priority
 * when outputing a character after it has been safely taken off the queue.
 * This will alow input interrupts to occur when writing massive amounts of
 * stuff to the screen.  The TS_BUSY flag will protect us from the input
 * interrupt echo from affecting the putc in progress.
 */

cnstart(tp)
register struct tty *tp;
{
	register int s = spl5();
	int cnostart();

	if ((tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) == 0) {
		tp->t_state |= TS_BUSY;			/* we are now busy */
		timeout(cnostart, tp, 1);		/* minimal delay */
	}
        splx(s);
}

/*
 * cnostart is entered at low priority from timeout routine
 * it then outputs the characters via the emulator
 * output routine for this screen.
 */
cnostart(tp)
	register struct tty *tp;
{
	register int c;
	register int scr_unit;
	register int s = spl5();		/* protect data structures */
	register SCREEN_INFO *si;
	int outcnt = 0;
	static int outlimit = 32;

	if ((scr_unit = MINOR_SCREEN(tp->t_dev)) == CONS_GEN)
		scr_unit = cons_display;

	si = &cons_info[scr_unit];
	while ((tp->t_outq.c_cc > 0) && ((c = getc(&tp->t_outq)) != -1)) {

		/* This is for delays in output.
		 * This happens if a delay is set (e.g. stty nl2)
		 */
		if ((tp->t_flags&(RAW|LITOUT)) == 0 && c > 0177) {
			timeout(ttrstrt, (caddr_t)tp, (c&0177));
			tp->t_state |= TS_TIMEOUT;
			goto lout;      /* clear busy, return */
		} else {
			splx(s);		/* Do output at low priority */
			(*emulsw[si->oute.emulator].e_putc)(c, si, 0);
			s = spl5();		/* Raise priority again */
			if (tp->t_state & TS_TTSTOP)
				break;		/* stop output for now */
			if ((tp->t_outq.c_cc > outlimit) && ++outcnt > outlimit) {
				timeout(cnostart, tp, 1);   /* minimal delay */
				splx(s);	/* go back to original priority */
				return;
			}
		}
	}

	if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
		if (tp->t_state&TS_ASLEEP) {
			tp->t_state &= ~TS_ASLEEP;
			wakeup ((caddr_t)&tp->t_outq);
		}
		if (tp->t_wsel) {
			selwakeup (tp->t_wsel, tp->t_state & TS_WCOLL);
			tp->t_wsel = 0;
			tp->t_state &= ~TS_WCOLL;
		}
	}
lout:
	tp->t_state &= ~TS_BUSY;
	splx(s);			/* go back to original priority */

}

/*
 * Print a character on console.
 * Attempts to save and restore device
 * status.
 */
cnputc(c)
register int c;
{
	register SCREEN_INFO *si = &cons_info[cons_display];

	/*
	 * Try waiting for the console tty to come ready,
	 * otherwise give up after a reasonable time.
	 */
	if (c == 0)
		return;

#ifdef ATR
	if (edebug&0x100)
		return(pc_req(CB_PUTC, c, PUTCENT));
#endif ATR

EDEBUG (0x01, aed_pr("cnputc (%c)\n", c));
	if (cons_tp && !(panicstr)) {
		if (c == '\n')
			cnforward_putc ('\r',si);
		cnforward_putc(c,si);
	} else {
		if (c == '\n')
			(*emulsw[si->oute.emulator].e_putc)('\r', si, SCREEN_KERNEL);
		(*emulsw[si->oute.emulator].e_putc)(c, si, SCREEN_KERNEL);
	}
EDEBUG (0x01, aed_pr("cnputc leaving\n"));

	cnputc(0);
}

cnforward_putc(c, si)
char c;
register SCREEN_INFO *si;
{
	if (cons_tp) {
		ttyoutput(c, cons_tp);
		ttstart (cons_tp);
	}
}

cnselect (dev, rw)
	dev_t dev;
	int rw;
{
	register int scr_unit;
	register SCREEN_INFO *si;

	if (minor(dev) == CONS_GEN) {
		scr_unit = cons_if;
	} else {
		scr_unit = MINOR_SCREEN (dev);
	}
	si = &cons_info[scr_unit];

	return ((*emulsw[si->ine.emulator].e_select)(scr_unit, rw, si));
}

#ifdef DEBUG
#include "aed.h"
#if defined(IBMRTPC) && NAED > 0
int aed_pr_debug = 1;
int first = 1;
aed_pr (s,d1,d2,d3)
char *s;
{
	register char   **p;
	char str[80];
	register char *pstr = str;

	if (!aed_pr_debug)
		return;

	if (first) {
		aed_screen_init ();
		first = 0;
	}
	p = &s;
	d_sprintf (str, p[0], p[1], p[2], p[3], p[4], p[5]);
	while (*pstr)
		aed_screen_putc (*pstr++,LITERAL_VIDEO);
	if (*(--pstr) == '\n')
		aed_screen_putc ('\r',LITERAL_VIDEO);
}
#else
aed_pr (s,d1,d2,d3)
{
#ifdef ATR
	if (edebug&0x100)
		printf(s,d1,d2,d3);		/* use printf */
#endif ATR
	return;
}
#endif
#endif
