static char rcsid[] = "$Header: vg.c,v 820.1 86/12/04 19:56:57 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

#include "vg.h"
#if NVG > 0

#include "../h/param.h"
#include "../s32dev/useful.h"
#include "../h/systm.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../s32dev/vg_ioctl.h"
#include "../s32dev/regmuck.h"
#include "../h/dir.h"
#include "../h/proc.h"
#include "../h/kernel.h"
#include "../h/conf.h"
#include "../h/uio.h"
#include "../h/user.h"
#include "../machine/cpu.h"
#include "../s32/vectors.h"
#include "../s32/debug.h"
#include "../h/map.h"
#define   NBD 1		/* number of board sets??? vpb.h should define this */
#include "../s32dev/vpb.h"
#include "../s32dev/vgvar.h"
#include "../s32dev/circbuf.h"
#include "../s32dev/console.h"

/**************************************************************************
     General Comments

     vg.c and vp.c danger areas

     I. Both vg.c and vp.c set and release pages map entries via
	setpagemap. The following needs to be guarenteed.

	a) All operations on the page/segment map must be performed
	   at level 7.

	   Suppose: 

	   Proc X (level n) sets the map context to modify the map and
	   sets modify high or low map. It then gets an interrupt before
	   it can set the context back to normal. Any memory operations
	   above 8 meg (2^23) virtual, will write into the map.

	b) If scratch pages SSEG1_VA and SSEG2_VA have been mapped, they
	   must be released/restored before the process goes to sleep.

        c) All setSeg calls must be followed by relSeg calls. If two
	   setSeg calls follow one another, then the value saved by the
	   first setSeg call will be lost. [This should be rewritten.
	   This was workable in a single board system, but in an
	   interrupt poling loop, this remap is costly.]

    II. There exist multiple TOCTU (time of check, time of use) races.
	The driver must raise its interrupt level to that of the 
	interrupt service procedure when checking the reader and writer
	pointers. If the user process is going to be placed to sleep,
	then it must stay at this high level until it is asleep. Otherwise
	after the check, the condition might change and an interrupt may
	be presented and the driver will wakeup sleeping processes. Once
	the interrupt service procedure has completed, the user process will
	go to sleep ... forever.

************************************************************************/


#define VGSLEEP PZERO+2

#ifdef	HIGHVGBOARDS
#define VG0BASE (char *)(0xF80000) /* Base addr of vg 0 */
#define VG1BASE (char *)(0xF90000)
#define VG2BASE (char *)(0xFA0000)
#define VG3BASE (char *)(0xFB0000)
#else	HIGHVGBOARDS
#define VG0BASE (char *)(0x3F0000) /* Base addr of vg 0 */
#define VG1BASE (char *)(0x3A0000)
#define VG2BASE (char *)(0x3B0000)
#define VG3BASE (char *)(0x3C0000)
#endif	HIGHVGBOARDS

#define CG0BASE (char *)(0xE00000) /* Base addr of CGB 0 */
#define CG1BASE (char *)(0xE40000)
#define CG2BASE (char *)(0xE80000)
#define CG3BASE (char *)(0xEC0000)

#define CBA0 (unsigned long)(0x8000) /* circular buffer area for vg0 */
#define CBA1 (unsigned long)(0xA000)
#define CBA2 (unsigned long)(0xC000)
#define CBA3 (unsigned long)(0xE000)

#define VGWATER 32  /* hi/lo water mark for circular buffers */

/*  Offsets to registers on VG Board memory */
#define VGSEGREG 0x4010  /* VG seg register */
#define CGSEGREG 0xC6F0  /* CG seg register */
#define VGSTART  0x4018  /* VG start 0x20 starts */
#define CGSTART 0x10000  /* CG start  0x0 starts */
#define VGSTOP   0x8000  /* VG stop 0x20 stops   */
#define CGSTOP  0x10000  /* CG stop  0x2 stops */
#define VGDMGR	 0x0024  /* VG location of display manager */
#define VGHMEM   0x0026  /* VG location of hard copy vg "mung" segment */
#define VGUPTA	 0x0028	 /* VG location of 4-bytes FWA (virtual) page tables */
#define VGMPA    0x002C  /* VG location of mulit-bus pram block */
#define VGPMBA   0x002E  /* VG loc of Private multi-bus param block */
#define VGUPTM	 0x0030	 /* VG location of 4-bytes ->Usrptmap */

char    *dmgrMmem;           /* Address of display manager multibus segment   */
char    *vgMmem;             /* Address of vg boards multibus segment   */
char    *vgHardCopyMem;      /* Address of multibus hardcopy buffer */

char vgscanning=0;

/* 
 * Array used to save scratch segment values 
 * Note:
 *    OpageEntry[x][0] = used to hold scratch seg SEG1_VA
 *    OpageEntry[x][1] = used to hold scratch seg SEG2_VA
 *  
 *    OpageEntry[0 or 1][x] = Scratch seg from upper half procedures
 *    OpageEntry[2 or 3][x] = Scratch seg from interrupt procedure
 */
unsigned short    OpageEntry[4][2];
#define   NONINTERSAVE	0
#define   INTERSAVE     2

/*
    VG Device word
    15,14,13,12,11,10,09,08,07,06,05,04,03,02,01,00
    |---------Major--------|LD|--|Board|---TTY#----|
*/


#define VGLOADDEV 0x80  /* VG board load device */
#define vgboardnum(y) (((y)>>4)&3)
#define vgttynum(y) ((y)&0xf)

/* Values for VG State */
#define NOTLOADED  0
#define FIRSTLOAD  1
#define LOADING    2
#define VGFREE     3



/* VG Board Control Structure */
struct Tvgcntl vgcntl[MAXVGB] =
{
	{VG0BASE, 0, 1},  /* 1==>default to window mode for testing */
	{VG1BASE, 0, 1},
	{VG2BASE, 0, 1},
	{VG3BASE, 0, 1},
};

int vgstart();
extern char partab[];
extern struct bdt vgbdt;

setSeg(Vaddr, Paddr)
    register int Vaddr;
    register int Paddr;
{
    setScratch(Vaddr, Paddr, NONINTERSAVE);
}

relSeg(Vaddr)
    register Vaddr;
{
    relScratch(Vaddr, NONINTERSAVE);
}

IsetSeg(Vaddr, Paddr)
    register int Vaddr;
    register int Paddr;
{
    setScratch(Vaddr, Paddr, INTERSAVE);
}

IrelSeg(Vaddr, Paddr)
    register Vaddr;
{
    relScratch(Vaddr, INTERSAVE);
}


setScratch(Vaddr, Paddr, savebase)
    register int Vaddr;
    register int Paddr;
    register int savebase;

    /* This procedure takes a physical address "Paddr" and maps to it
     * using the virtual page descriptors Vaddr and Vaddr+1.
     * Vaddr should be in the set {SSEG1_VA, SSEG2_VA}.
     * The physical addresses pointed to by these page map entres are 
     * saved in the array OpageEntry.
     * 
     * Note that the scratch segments from the interrupt procedure are
     * saved in another location. This is necessary, otherwise the interrupt
     * proc may overwrite scratch segment info.
     */

{
    register int i;
    short s;

    if (Vaddr<SSEG2_VA)         
	i = 0 + savebase;
    else
	i = 1 + savebase;

    s = spl7();
    OpageEntry[i][0] = setpagemap((Vaddr>>pageshift) ,  (Paddr>>pageshift));
    OpageEntry[i][1] = setpagemap((Vaddr>>pageshift)+1, (Paddr>>pageshift)+1);
    splx(s);
}    

relScratch(Vaddr, savebase)
    register int Vaddr;
    register int savebase;
{

    register int i;
    short s;

    if (Vaddr<SSEG2_VA)         
	i = 0 + savebase;
    else
	i = 1 + savebase;

    s = spl7();
    setpagemap((Vaddr>>pageshift)  , OpageEntry[i][0]);
    setpagemap((Vaddr>>pageshift)+1, OpageEntry[i][1]);
    splx(s);
}
    
sPset(Paddr, value)
    int   Paddr;
    short value;

    /* this procedure sets physical address "Paddr" to SHORT value "value" */
{
    setSeg(SSEG1_VA, Paddr);
    *((short *) ((Paddr&0xFFF) + SSEG1_VA)) = value;
    relSeg(SSEG1_VA);
}

vgopen(dev, flag)
{
	register struct Tvgcntl *vgp;
	extern          vgint();
	short           s;
	unsigned long   offset;
	register char  *cptr;
	short           ttynum;
	label_t         jb;
	label_t            *saved_jb;
	extern label_t     *nofault;

	DODEBUG(D_VG,("vgopen dev=%x flag=%x pid=%x\n",dev,flag,u.u_procp->p_pid));
	vgp = &vgcntl[vgboardnum(dev)];
	if (dev&VGLOADDEV) {
		if (vgp->vgstate == NOTLOADED) {
			if (vgp->vgtype=='C') {/* Color Graphics Board */
				vgp->vgbase=(char *)(CG0BASE+(vgboardnum(dev)<<18));
				vgp->vgsegreg=CGSEGREG;
				vgp->vglstart=(short *)(vgp->vgbase+CGSTART);
				vgp->vglstop=(short *)(vgp->vgbase+CGSTOP);
				vgp->vgstartb=0; vgp->vgstopb=2;
			} else if (vgp->vgtype=='V') {/* Video Graphics Board */
				/* vgp->vgbase=(char *)(VG0BASE+(vgboardnum(dev)<<16)); */
				vgp->vgsegreg=VGSEGREG;
				vgp->vglstart=(short *)(vgp->vgbase+VGSTART);
				vgp->vglstop=(short *)(vgp->vgbase+VGSTOP);
				vgp->vgstartb=0x20; vgp->vgstopb=0x20;
			} else panic("vgopen vgtype");
			/* Check if board is present */
			saved_jb = nofault;
			if (!setjmp(&jb)) {nofault = &jb; sPset(vgp->vgbase,0);}
			else {nofault=saved_jb; return(EFAULT);}
			nofault = saved_jb;
			dmgrMmem=(char *)vgbdt.b[0].privMba;
			if (dmgrMmem==0) return(EFAULT);
			vgMmem=dmgrMmem+0x20000;
		} else if (vgp->vgstate == VGFREE) { /* Stop the board */
			sPset(vgp->vglstop, vgp->vgstopb);
		} else return(EFAULT);
		vgp->vgstate = FIRSTLOAD;
		vgp->vgreload++;  /* a new generation */
		{
			register int k;
			for (k=16; --k>=0; ) {/* tell the old generation */
				if (k<MAXTTYS) {
					register struct tty *tp= &vgp->vgtty[k];
					tp->t_state&=~TS_ASLEEP;
					wakeup((caddr_t)&tp->t_outq);
				}
				vgp->vgisleep=0; vgp->vgosleep=0;
				vgp->vgrcvseq[k]=0; vgp->vgxmtseq[k]=0;
				wakeup(&vgp->vgrcvseq[k]);
			}
		}
		vgp->vgCBloc=CBA0+(vgboardnum(dev)<<13)+vgMmem;
		return(0);
	}
	if (vgp->vgstate<VGFREE) return(EFAULT);  /* board not loaded */
	if (MAXTTYS>(ttynum=vgttynum(dev))) {/* Open of tty */
		register struct tty *tp = &vgp->vgtty[ttynum];
		DODEBUG(D_VG,("vgopen tty dev=%x, ttynum=%d, vgstate=%d\n",
			dev, ttynum, vgp->vgstate));
		tp->t_addr=0;
		tp->t_oproc=vgstart;
		if ((tp->t_state&TS_ISOPEN) == 0) {
			tp->t_state = TS_WOPEN|TS_CARR_ON;
			tp->t_flags = EVENP|ODDP|ECHO|XTABS|CRMOD|
					CRTBS|CRTERA|CRTKIL|CTLECH;
			tp->t_ispeed = tp->t_ospeed = B9600;
			ttychars(tp);
			vgtodmgr(tp,TIOCSETC,0,0);
		}
		tp->t_line = NTTYDISC;
		return((*linesw[tp->t_line].l_open)(dev, tp));
	}
	if (ttynum==VGCOMDEV) return(0);
	return(ENODEV);
}

vgclose(dev, flag)
{
	register struct Tvgcntl *vgp;
	short    ttynum;

	DODEBUG(D_VG,("vgclose dev=%x flag=%x pid=%x\n",dev,flag,u.u_procp->p_pid));
	vgp = &vgcntl[vgboardnum(dev)];
	if (dev&VGLOADDEV) {
		register int i;
		register struct cb1 *cbp;
		/* init circular buffer pointers */
		setSeg(cbp=(struct cb1 *)SSEG1_VA,vgp->vgCBloc);
		for (i=32; --i>=0; cbp++)
			{cbp->cb1in=2; cbp->cb1out=2;}
		relSeg(--cbp);
		DODEBUG(D_VG,("vgclose: 8086 loaded\n"));
		/* Start vg seg address & go command */
		sPset(vgp->vgbase,vgp->vgtype=='C');
		sPset(vgp->vgbase+vgp->vgsegreg, (short)(((int)vgMmem)>>16)); 
		sPset(vgp->vgbase+VGDMGR, (short)(((int)dmgrMmem)>>16)); 
		sPset(vgp->vgbase+VGHMEM, (short)(((int)vgHardCopyMem)>>16)); 

		/*
		 * Pass a pointer to the pte-s
		 * for the current process.
		 */
		sPset(vgp->vgbase+VGUPTA,   (short)(((long)usrpt)>>16));
		sPset(vgp->vgbase+VGUPTA+2, (short)	   usrpt);
		sPset(vgp->vgbase+VGPMBA, vgp->vgCBloc);

		/*
		 * Pass the address of the (entire)
		 * user page table map.
		 * The map contains the pte
		 * for the pte-s for a process.
		 */
		sPset(vgp->vgbase+VGUPTM  ,((int)&Usrptmap[0])>>16);
		sPset(vgp->vgbase+VGUPTM+2,      &Usrptmap[0]);

		sPset(vgp->vglstart, vgp->vgstartb);
		DODEBUG(D_VG,("vgopen: 8086 running\n"));
		vgp->vgstate = VGFREE;
		if (vgscanning==0) {/* start scanning circular buffers */
			extern vgscan();
			vgscanning=1; timeout(vgscan,0,hz*3/100);
		}
		return;
	}
	if (MAXTTYS>(ttynum=vgttynum(dev))) {/* close of tty */
		register struct tty *tp = &vgp->vgtty[ttynum];
		/* wflushtty(tp); /* already in l_close */
		(*linesw[tp->t_line].l_close) (tp);
		ttyclose(tp);
	}
	if (ttynum==VGCOMDEV) {
		wakeup(vgp);
		return;
	}
}

extern char *stkalloc();

vgwrite(dev,uio)
register struct uio *uio;
{
	register struct Tvgcntl *vgp= &vgcntl[vgboardnum(dev)];
	register struct cb1 *cbp;
	register int k,n,ttynum,va;

	if (dev&VGLOADDEV) {
		vgp->vgstate=LOADING;
		while (uio->uio_resid>0) {
			register int k=uio->uio_offset;
			if (k<0x10000) k+=(int)vgp->vgbase;  /* onboard */
			else k+=(int)vgMmem-0x10000;  /* offboard */
			setSeg(SSEG1_VA, k);
			k&=pagemask;
			k=uiomove(SSEG1_VA+k,pagesize-k,UIO_WRITE,uio);
			relSeg(SSEG1_VA);
			if (k) return(k);
		}
		return(0);
	}
	k=1;  /* minimum size for cb1put */
	if (VGACKDEV==(ttynum=vgttynum(dev))) {/* internal write from vgioctl */
		ttynum=VGCOMDEV;
		k=uio->uio_resid;  /* atomic write */
	} else if (VGCOMDEV==ttynum) {
		register struct vgcmd {
			char vg_gflag;
			unsigned char vg_seq;
			char cblock[CBLKSIZE];
			long vg_p0br;
		} *p;
		struct iovec iov;
		struct uio vio;

		ttynum=0xf&u.u_ttyd;
		if (uio->uio_resid!=CBLKSIZE) return(ENOMEM);
		p=(struct vgcmd *)stkalloc(sizeof(*p));
		if (k=uiomove(p->cblock,CBLKSIZE,UIO_WRITE,uio)) return (k);
		uio= &vio;
		p->vg_gflag=0xfe;
		p->vg_seq= ++(vgp->vgxmtseq[ttynum]);
		p->vg_p0br=vtop(&u.u_pcb.pcb_p0br);
		iov.iov_base=(caddr_t)p;
		iov.iov_len=sizeof(*p);
		uio->uio_iov= &iov;
		uio->uio_iovcnt=1;
		uio->uio_offset=0;
		uio->uio_segflg=1;  /* kernel address for *p */
		uio->uio_resid=sizeof(*p);
		k=sizeof(*p);  /* atomic write */
	}
	va=(int)vgp->vgCBloc + 16*256 + ttynum*sizeof(*cbp);
	cbp=(struct cb1 *)(SSEG1_VA+(pagemask&va));
	while (uio->uio_resid>0) {
		register char *buf;
		for (;;) {
			register char vggen=vgp->vgreload;
			setSeg(cbp,va);
			n=cbp->cb1out-cbp->cb1in; if (n<=0) n+=256-2; --n;
			if (n>=k) break;
			relSeg(cbp);
			n=spl1();
			vgp->vgosleep|=1<<ttynum;
			sleep(&vgp->vgxmtseq[ttynum], TTOPRI);
			splx(n);
			if (vggen!=vgp->vgreload) return(ENXIO);
		}
		if (n>uio->uio_resid) n=uio->uio_resid;
		buf=stkalloc(n);
		if (k=uiomove(buf,n,UIO_WRITE,uio)) return(k);
		cb1put(cbp,buf,n);
		stkfree(n);
		relSeg(cbp);
		k=256-2-VGWATER; /* 2nd time insist on big swallow */
		if (k>uio->uio_resid) k=uio->uio_resid; /* unless small morsel*/
	}
	return(0);
}

vgread(dev,uio)
struct uio *uio;
{
	struct Tvgcntl *vgp = &vgcntl[vgboardnum(dev)];
	register int ttynum = vgttynum(dev);
	register int n, va=(int)vgp->vgCBloc+ttynum*sizeof(struct cb1);
	register struct cb1 *cbp=(struct cb1 *)(SSEG1_VA+(pagemask&va));
	register struct tty *tp = &vgp->vgtty[ttynum];
	register char *buf;
	while (setSeg(cbp,va), cbp->cb1in==cbp->cb1out) {
		register char vggen=vgp->vgreload;
		register int s;
		relSeg(cbp);
		s=spl1();
		vgp->vgisleep|=(1<<ttynum);
		sleep(&vgp->vgrcvseq[ttynum],VGSLEEP);
		splx(s);
		if (vggen!=vgp->vgreload) return(ENXIO);
	}
	n=cb1full(cbp);
	if (n>uio->uio_resid) n=uio->uio_resid;
	buf=stkalloc(n);
	if (0==(tp->t_flags&RAW)) {/* EOF processing (rats!) */
		register char *p;
		register int i;
		cb1peek(cbp,buf,n);
		for (p=buf, i=n; --i>=0; )
			if (*p++==0xFE) {n=p-buf-1; break;}
		cb1get(cbp,buf,p-buf);
	} else  cb1get(cbp,buf,n);
	relSeg(cbp);
	return(uiomove(buf,n,UIO_READ,uio));
}

vgintr()
{
	register int i;
	short    s;
	short    readrequest;

	DODEBUG(D_VG,("vgintr\n"));
}

/* this relic needed only for kernel printf to user (file system full, etc.) */
vgstart(tp)
	register struct tty *tp;
{
	DODEBUG(D_VG,("Start tp = %x, with cc = %d\n",tp,tp->t_outq.c_cc));
		/* check TS_ASLEEP because ttinput() can call us to echo
		/* even when output is full.
		*/
	while (0<tp->t_outq.c_cc && !(tp->t_state&TS_ASLEEP)) {
		register struct Tvgcntl *vgp = &vgcntl[vgboardnum(tp->t_dev)];
		struct cb1 *cbp = (struct cb1 *)(SSEG1_VA + vgttynum(tp->t_dev)*sizeof(*cbp));
		register char *p;
		register int c,j,n;
		char *buf;
		char vggen=vgp->vgreload;
		setSeg(cbp, vgp->vgCBloc + pagesize);
		while (0==(j=cb1room(cbp))) {
			register int s;
			relSeg(cbp);
			if (vgscanning&2) return; /* can't sleep in interrupt */
			s=spl1();
			tp->t_state |= TS_ASLEEP;
			sleep((caddr_t) &tp->t_outq, TTOPRI);
			splx(s);
			if (vggen!=vgp->vgreload) return(ENXIO);
			setSeg(cbp, vgp->vgCBloc + pagesize);
		}
		for (p=buf=stkalloc(n=j); --j>=0 && (c=getc(&tp->t_outq))>=0; ) 
			*p++=c;
		cb1put(cbp,buf,p-buf);
		stkfree(n);
		relSeg(cbp);
	}
}


vgioctl(dev, cmd, data, flag)
	caddr_t data;
{
	struct Tvgcntl *vgp = &vgcntl[vgboardnum(dev)];
	register int ttynum=vgttynum(dev);

	if (ttynum==VGCOMDEV) {
		if (cmd == VGFLUSH) return(vgflush(dev));
		else return(vgtodmgr(&vgp->vgtty[0], cmd, data, flag));
	}
	if (MAXTTYS>ttynum) {
		register struct tty *tp = &vgp->vgtty[ttynum];
		register int error;
		error=(*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
		if (error>=0) return(error);
		if (cmd==FIONREAD) {
			register struct cb1 *cbp=ttynum+(struct cb1 *)SSEG1_VA;
			setSeg(cbp,vgp->vgCBloc);
			*(off_t *)data=cb1full(cbp);
			relSeg(cbp);
			return(0);
		} else if (cmd!=TIOCSTI) error=ttioctl(tp,cmd,data,flag);
		if (error<0) return(ENOTTY);
		return(vgtodmgr(tp,cmd,data,flag));  /* tell dmgr */
	}
	return(ENXIO);
}

vgtodmgr(tp,com,data,flag)
register struct tty *tp;
caddr_t data;
{
	struct dmgrpkt {
		char opcode;  /* what to do */
		char ttyn;    /* which channel */
		char ii,io,oi,oo;  /* circular buffer pointers at the time */
		short tshort; /* random param */
		long flags;    /* t_flags */
		struct ttychars tc;
	} dmpkt;

	switch (com) {
		struct iovec iov;
		struct uio vio;
	case VGWINDOW:
		dmpkt.tshort= *(int *)data; goto L100;
	case TIOCSTI:
		dmpkt.tshort= *(char *)data; goto L100;
	case TIOCFLUSH: case TIOCSETD:
		dmpkt.tshort= *(short *)data;
    L100:
	case TIOCSETP: case TIOCSETN:
	case TIOCCSET: case TIOCSETC: case TIOCSLTC:
	case TIOCLBIS: case TIOCLBIC: case TIOCLSET:
	case TIOCBIS:  case TIOCBIC:  case TIOCSET:
	case TIOCSTOP: case TIOCSTART:
		dmpkt.flags=tp->t_flags;
		dmpkt.tc=tp->t_chars;
		dmpkt.opcode=com;
		dmpkt.ttyn=vgttynum(tp->t_dev);
		dmpkt.ii=0; dmpkt.io=0; dmpkt.oi=0; dmpkt.oo=0;

		iov.iov_base=(caddr_t)&dmpkt;
		iov.iov_len=sizeof(dmpkt);
		vio.uio_iov= &iov;
		vio.uio_iovcnt=1;
		vio.uio_offset=0;
		vio.uio_segflg=1;  /* kernel address for dmpkt */
		vio.uio_resid=sizeof(dmpkt);
		return(vgwrite(tp->t_dev-dmpkt.ttyn+VGACKDEV,&vio));
	}
	return(0);
}

#define NFLCOUNT 9
short vgflcount[NFLCOUNT+1];

vgflush(dev)
{
	register short s;
	register int vgtty;
	register struct Tvgcntl *vgp = &vgcntl[vgboardnum(dev)];
	register i = 0;

	vgtty=0xf&u.u_ttyd;
	s=spl1();
	while (vgp->vgxmtseq[vgtty]!=vgp->vgrcvseq[vgtty]) {
		register char vggen=vgp->vgreload;
		/* vgisleep because we wait for VGACK, which is an input */
		vgp->vgisleep|=(1<<VGACKDEV);
		sleep(&vgp->vgrcvseq[VGACKDEV],VGSLEEP);
		if (i < NFLCOUNT) ++i;		/* sbs */
#ifdef notdef
		if (vggen!=vgp->vgreload) return(ENXIO);
#else notdef
		if (vggen!=vgp->vgreload) {	/* sbs 850921 */
			splx(s);
			return(ENXIO);
		}
#endif notdef
	}
	splx(s);
	++vgflcount[i];
	return(0);
}

int	vghz = 4;

vgscan()
{
	register struct Tvgcntl *vgp;
	vgscanning|=2;
    for (vgp= &vgcntl[0]; vgp<&vgcntl[MAXVGB]; vgp++) {
	register struct tty *tp;
	register struct cb1 *cbp=(struct cb1 *)SSEG1_VA;
	register int i,n,msk;
	if (vgp->vgstate<VGFREE) continue;
	IsetSeg(cbp,vgp->vgCBloc);  /* note that this maps 8K */
	for (tp=vgp->vgtty,msk=1,i=0; i<16; msk<<=1,cbp++,tp++,i++){
		if (i<MAXTTYS && !(tp->t_state&TS_ISOPEN)) continue;
		n=cbp[16].cb1in-cbp[16].cb1out;
	        if (0==(n&((256-1)&~(VGWATER-1)))) {/* output nearly empty */
			if (vgp->vgosleep&msk) {
				vgp->vgosleep&=~msk;
				wakeup(&vgp->vgxmtseq[i]);
			}
                }
		if (cbp->cb1in!=cbp->cb1out) {/* input not empty */
			DODEBUG(D_VG,("vgtty input %x %x\n",cbp,tp));
			if (vgp->vgisleep&msk) {
				vgp->vgisleep&=~msk;
				wakeup(&vgp->vgrcvseq[i]);
			}
			if (i<MAXTTYS) {/* only window tty has *tp */
				if (tp->t_rsel) {
					selwakeup(tp->t_rsel, tp->t_state&TS_RCOLL);
					tp->t_state &= ~TS_RCOLL;
					tp->t_rsel = 0;
				}
				if (constate==CON_UNASSIGNED && concandidate(tp->t_dev))
					conassign(tp);
			} else if (i==VGACKDEV) {/* supervision */
				unsigned char buf[4]; /* flag, tty, seq, pad */
				register int ttyn;
				register unsigned char *p;
				register n;			/* sbs */
				register keepscanning;		/* sbs */
/*
 * sbs 850921
 * Cost of scanning is 40 ms (very slow, relative to GED and VG board).  So,
 * try to get as many drawing-ACK packets as is convenient.  Don't want to do
 * too much work here, though... may need an upper bound on n.
 */
				n=cb1full(cbp);		/* only ask 1st time */
top:
				if (sizeof(buf)>n) continue;
				keepscanning=0;
				cb1get(cbp,p=buf,sizeof(buf)); n-=sizeof(buf);
				ttyn=0xf&p[1];
				switch (p[0]) {
				  default: panic("vgscan ack?"); break;
				  case 0xfe:
					vgp->vgrcvseq[ttyn]=p[2];
					keepscanning=1;
					break;
				  case SIGINT: case SIGQUIT: case SIGTSTP: {
				  	struct tty *tp2= &vgp->vgtty[ttyn];
					gsignal(tp2->t_pgrp,p[0]);
					if (0==(tp2->t_flags&NOFLSH)) {
						register struct cb1 *cbp2;
						cbp2=(struct cb1 *)SSEG1_VA;
						cbp2+=ttyn;
						cbp2->cb1out=cbp2->cb1in;
					}
				  }
				}
				if (keepscanning) goto top;
	        	}
		}
	}
	IrelSeg(SSEG1_VA);
    }
	vgscanning&=~2;
	timeout(vgscan,0,vghz/* HZ*4/100 */);
}

#include "../h/file.h"
vgselect(dev, rw)
	dev_t dev;
	int rw;
{
	register struct Tvgcntl *vgp= &vgcntl[vgboardnum(dev)];
	register int ttynum=vgttynum(dev);
	register struct tty *tp = &vgp->vgtty[ttynum];
	register struct cb1 *cbp=(struct cb1 *)(SSEG1_VA+ttynum*sizeof(*cbp));
	register int k;
	register int nread;
	register int s = spl1();

	setSeg(cbp,vgp->vgCBloc);
	switch (rw) {
	case FREAD:
		if (cbp->cb1in!=cbp->cb1out)
			goto win;
		if (tp->t_rsel && tp->t_rsel->p_wchan == (caddr_t)&selwait)
			tp->t_state |= TS_RCOLL;
		else
			tp->t_rsel = u.u_procp;
		break;

	case FWRITE:
		k=cbp[16].cb1in; ++k; k+=(k>>7)<<1; k&=0xFF;
		if (k!=cbp[16].cb1out)
			goto win;
		if (tp->t_wsel && tp->t_wsel->p_wchan == (caddr_t)&selwait)
			tp->t_state |= TS_WCOLL;
		else
			tp->t_wsel = u.u_procp;
		break;
	}
	relSeg(cbp); splx(s);
	return (0);
win:
	relSeg(cbp); splx(s);
	return (1);
}

#endif NVG > 0
