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

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

/* Routines for a buffering output emulator.
 */
#include "param.h"
#include "conf.h"
#include "ioctl.h"
#include "tty.h"
#include "errno.h"

#include "screen_conf.h"
#include "consdefs.h"
#include "consvars.h"
#include "buf_emul.h"
/*
 * defines to supprot the IOCTL
 */
#include "dir.h"
#include "user.h"
#include "proc.h"
#include "../ca/io.h"

extern struct user u;

#ifdef ATR
#include "../ca_atr/pcif.h"
#include "vga.h"
#include "ibmvbarmmmdxiv.h"
#else
/*
 * non-atr devices don't have 8514's and vga's
 */
#define NIBMVBARMMMDXIV 0
#define NVGA 0
#endif !ATR

#ifdef AT
#include "aed.h"
#else
#define NAED 0
#endif /* AT */


/* limit on number of characters */ 
#define buf_maxchar  ((BUF_CMAX/CBSIZE) * CBSIZE)

struct buf_softc buf_softc[NUMBER_CONS];


buf_open(dev, tp, si)
int dev;
register struct tty *tp;
register SCREEN_INFO *si;
{
	register struct clist *b = &(buf_softc[WS].buf_cq);
	int	s;

	if ((buf_softc[WS].buf_flag & BUF_ISOPEN) == 0) {
		buf_softc[WS].buf_flag = BUF_ISOPEN;
		b->c_cc = 0;
		b->c_cf = b->c_cl = 0;
		buf_softc[WS].buf_proc = u.u_procp;
		buf_softc[WS].buf_save_state = BUF_SCREEN_OK;
	}
#ifdef ATR
	s = spl5();
	while (!screen_in_foreground(WS)) {
		buf_softc[WS].buf_flag |= BUF_ISASLEEP;
		/* 
		 * Sleep if we are in the background. NOTE:
		 * buf_open cannot be allowed to be called off
		 * interrupt level! This is prevented if buf_emul
		 * is never a CE_DEFAULT emulator.
		 */
		sleep(&buf_softc[WS],TTIPRI);
	}
	splx(s);
#endif ATR
	return (0);
}

buf_write(tp, uio, si)
register struct tty *tp;
struct uio *uio;
register SCREEN_INFO *si;
{
	return ((*linesw[tp->t_line].l_write)(tp, uio));
}

/*
 * output a character to the buffer.
 *	If a kernel panic is detected, restore console functions,
 *	flush buffer, and allow panic processing to proceed.
 */

extern char *panicstr;

buf_putc(c, si)
register int c;
register SCREEN_INFO *si;
{
	register struct clist *b = &buf_softc[WS].buf_cq;

	if ((b->c_cc > buf_maxchar) || (putc(c, b)))
		ndflush(b, CBSIZE);
	
	if (panicstr) /* do this after putting char in buffer */
		panic_restore(WS);
	return(0);	
}

/* 
 *	pass back a character from buffer
 */

buf_getc(which)
register int which;
{
	register struct clist *b = &buf_softc[which].buf_cq;

	return(getc(b));	/* which will be -1 if empty */
}

static int buf_force_sleep = 0;	/* set to 1 to force sleep */
buf_ioctl(tp, cmd, data, si)
	struct tty *tp;
	int    cmd;
	unsigned	*data;
	SCREEN_INFO *si;
{
	int	s;

	switch (cmd) {

	case BUFSETWIND :
#if NVGA > 0 || (defined(AT) && (NEGA > 0 || NAED > 0))
		/*
		 * restrict the setting of the window to display
		 * memory.
		 */
		if ((*data < VGA_SCREEN_MIN) || (*data > VGA_SCREEN_MAX)) {
			return(EINVAL); /* - Invalid argument */
		}
		*data = set_128_window(*data);
		u.u_pcb.pcb_win = (char) current_128_w;
		return(0);
#else VGA
		return(EINVAL);	/* only the VGA need access to pc memory */
#endif VGA
/*		break;	*/

	case BUFINITVGA:
#if NVGA > 0
	{
		/*
		 * initialize the VGA to a predetermined mode
		 */
		unsigned mode = *(unsigned *)data;

		if (mode  >= VGA_MAX_MODE) {
			return(EINVAL);
		}
		vga_set_mode(mode);
		return(0);
	}
#else VGA
		return(ENXIO);
#endif VGA
/*		break;	*/
	case BUFINIT8514:
#if NIBMVBARMMMDXIV > 0
		/*
		 * this variable is set in ibm8514 if the display adapter
		 * exists
	 	 */
		if ((screen_sw[CONS_IBM8514].flags & CONSDEV_ALIVE) ==
							 	CONSDEV_ALIVE) {
			lo_init_8514();
			return(0);
		} else {
			return(ENXIO);
		}
#else 8514
		return(ENXIO);
#endif 8514
/*		break;	*/
	case BUF8514CTRL:
#if NIBMVBARMMMDXIV > 0
	{
		unsigned tmp;

		/*
		 * disable the 8514
		 */
		tmp = inw(PCIF_POS_REG);
		if (*data) {
			tmp |= 1;
		} else {
			tmp &= ~1;
		}
		outw(PCIF_POS_REG,tmp);
		return(0);
	}
#else 8514
		return(ENXIO);
#endif 8514
/*		break;	*/
	case BUFGETSAVE:
		*(int *)data = buf_softc[WS].buf_save_state;
		break;
	case BUFRESTOREDONE:
#ifdef ATR 
		s = spl5();
#else 
		s = spl1();
#endif ATR
		if (buf_softc[WS].buf_save_state == BUF_NEED_RESTORE) {
			buf_softc[WS].buf_save_state = BUF_IS_RESTORE;
		} else {
			splx(s);
			return(EINVAL);
		}
		splx(s);
		break;
	case BUFSAVEDONE:
#ifdef ATR 
		s = spl5();
#else 
		s = spl1();
#endif ATR
		if (buf_softc[WS].buf_save_state == BUF_NEED_SAVE) {
			buf_softc[WS].buf_save_state = BUF_IS_SAVE;
			if (buf_force_sleep) {
				buf_softc[WS].buf_flag |= (BUF_ISASLEEP);
				while (buf_softc[WS].buf_flag & BUF_ISASLEEP) {
					sleep(&buf_softc[WS],TTIPRI);
				}
			}
		} else {
			splx(s);
			return(EINVAL);
		}
		splx(s);
		break;
	case BUFSAVEWAIT:
#ifdef ATR 
		s = spl5();
#else 
		s = spl1();
#endif ATR
		if (buf_softc[WS].buf_save_state == BUF_NEED_SAVE) {
			buf_softc[WS].buf_save_state = BUF_IS_SAVE;
			buf_softc[WS].buf_flag |= (BUF_ISASLEEP|BUF_WILLRESTORE);
			while (buf_softc[WS].buf_flag & BUF_ISASLEEP) {
				sleep(&buf_softc[WS],TTIPRI);
			}
		} else {
			splx(s);
			return(EINVAL);
		}
		splx(s);
		break;
	default:
		return(-1);
	}
	return(0);
}

/*
 * save and restore the screen.
 * return 1 if saved, 0 if not.
 */
buf_save(tp,si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	switch (buf_softc[WS].buf_save_state) {
		case BUF_NEED_SAVE:
			break;
		case BUF_IS_SAVE:
			return(1);	/* buffer has been saved */
		case BUF_NEED_RESTORE:
		case BUF_IS_RESTORE:
		case BUF_SCREEN_OK:
			/* only send one signal at a time - catch it next time */
			if ((buf_softc[WS].buf_proc->p_sig & sigmask(SIGRETRACT)) == 0) {
				buf_softc[WS].buf_save_state = BUF_NEED_SAVE;
				psignal(buf_softc[WS].buf_proc,SIGRETRACT);
			}
			return(0);
		default:
			/* we're lost, better not hang the system waiting
			 * for us...
			 */
			return(1);
			/*break;*/
	}
	return(0);
}

/*
 * restore the screen
 * return 1 if done, 0 if not.
 */
buf_restore(tp,si)
register struct tty *tp;
register SCREEN_INFO *si;
{
	/*
	 * wake up any sleeping processes waiting for the bus
	 */
	if (buf_softc[WS].buf_flag & BUF_ISASLEEP) {
		buf_softc[WS].buf_flag &= ~BUF_ISASLEEP;
		wakeup(&buf_softc[WS]);
		if (buf_softc[WS].buf_flag & BUF_WILLRESTORE) {
			buf_softc[WS].buf_flag &= ~BUF_WILLRESTORE;
			buf_softc[WS].buf_save_state = BUF_NEED_RESTORE;
			return(0);
		} else
		if (!buf_force_sleep)
		{
			buf_softc[WS].buf_save_state = BUF_IS_RESTORE;
			return(1);
		}
	}

	/*
	 * no one was asleep, use the signals
	 */
	switch (buf_softc[WS].buf_save_state) {
		case BUF_NEED_RESTORE:
			return(0);
			/*break;*/
		case BUF_IS_RESTORE:
			return(1);	/* buffer has been saved */
		case BUF_NEED_SAVE:
		case BUF_IS_SAVE:
		case BUF_SCREEN_OK:
			/* only send one signal at a time - catch it next time */
			if ((buf_softc[WS].buf_proc->p_sig & sigmask(SIGGRANT)) == 0) {
				buf_softc[WS].buf_save_state = BUF_NEED_RESTORE;
				psignal(buf_softc[WS].buf_proc,SIGGRANT);
			}
			return(0);
		default:
			/* we're lost, better not hang the system waiting
			 * for us...
			 */
			return(1);
			/*break;*/
	}
}

/*
 *	On closing a display device, if it was the current console, then
 *	reopen it, flushing any messages which were queued.
 */
buf_dump_console(which)
register int which;
{
	register SCREEN_INFO *si = &cons_info[which];
	register int c;

	/* now dump buffer */
	while ((c = buf_getc(which)) != EOBUF) {
		if (c == '\n')
			(*emulsw[si->oute.emulator].e_putc)('\r',si,0);
		(*emulsw[si->oute.emulator].e_putc)(c,si,0);
	}

	buf_softc[which].buf_flag = 0;
	screen_sw[which].flags &= ~CONSDEV_NOINPUT;
}
/* process a kernel panic -- whichever was the active console, close its
 *	device, restore the console function, dump the message buffer.
 *	Then the panic printf is free to proceed
 */
panic_restore(which)
register int which;
{
	register SCREEN_INFO *si = &cons_info[which];
	register struct tty *tp = &cons[which];

	/* Close the Emulator just in case it isn't the default glass tty */
	(*emulsw[si->oute.emulator].e_close)(tp, si);

	/* Reinit the screen */
	sichars (which, CE_DEFAULT, si);
	(*screen_sw[WS].init)();
	screen_sw[WS].flags |= CONSDEV_INIT;

	/* Open the default emulator */
	(*emulsw[si->oute.emulator].e_open)(which, tp, si);

	buf_dump_console (which);
}
