static char rcsid[] = "$Header: vp.c,v 820.1 86/12/04 19:57:05 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 "vp.h"
#if NVP > 0
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/file.h"
#include "../h/user.h"
#include "../s32/vectors.h"
#include "../machine/cpu.h"
#include "../h/map.h"
#include "../s32dev/vpreg.h"
#include "../s32/debug.h"	/* sbs -- was s32dev */
#include "../h/uio.h"		/* sbs */
#include "../h/kernel.h"	/* sbs */

vpopen(dev, flag)
	dev_t	dev;
{
	short s;
	register char *dest;

	label_t jb, *saved_jb;
	extern label_t *nofault;

	/* Structure of arguments of open call */
	register struct args {
		char	*path;
		int	mode;
	} *uap;

	switch (minor(dev)) {
	case VPLOADDEV:
		if (vpstate == NOTLOADED) { /* Check if board is present */
			saved_jb = nofault;
			if (!setjmp(&jb)) {
				nofault = &jb;
				sPset((short *)VPBASE, 0);
			} else {
				nofault = saved_jb;
				u.u_error = EFAULT;
				DODEBUG(D_PIB,("PIB at 0x%x not present\n",
								VPBASE));
				break;
			}
			nofault =  saved_jb;
			vgvpMalloc();	/* setup vg+vp common segment */
			if (vpMmem == 0) {
				DODEBUG(D_PIB,("No mem\n"));
				u.u_error = ENOMEM;
				break;
			}
			vpstate = FIRSTLOAD;
			DODEBUG(D_PIB,("vpopen: open load dev ok\n"));
		} else {
			sPset(vpStop,0x20); vpstate=FIRSTLOAD;
		}
		break;

	case VPVPLOT: case VPVPRINT:		/* versatec */
		if (vpstate == RUNNING) {
			u.u_error=hcmopen(dev,flag);
			vpstate = BUSY;
			DODEBUG(D_PIB,("versatec open, vpMmem=0x%x flag=0x%x\n",
						((int) vpMmem)>>16, flag));
			if (flag&FWRITE) vpioctl(dev,BUFFPRINT,0,0);
		} else u.u_error = EFAULT;
		break;

	case DR11DEV: {
		register char *ap;
		register char *vmsp;
		register char *cnamep;
		register short c;
		register short i;
		short int haveBracket = 0; /* help with VMS name construction */
		short int haveDev = 0;	 /* help with VMS name construction */

		if (dr11state != DR11FREE) {
			u.u_error = ENODEV; break;
		}
		if (flag&FREAD && flag&FWRITE) {
			u.u_error = EFAULT; break;
		}
		dr11state=DR11INUSE;
		vmsname[0] = 0;
		if (0==(uap=(struct args *)u.u_ap)) {
 			u.u_error=EFAULT; dr11state=DR11FREE; break;
		}
		ap = uap->path;
		DODEBUG(D_PIB,("vpopen/dr11: path = %s\n",ap));
		/* Process path name  
		* "/dev/vms/[vaxnode::/][vaxdisk:/]vaxdir/vaxdir/file.extension"
		*/
		/* Gobble Slashes */
		do c = fubyte(ap++); 
		while ((c == '/') && (c != '\0'));

		/* Gobble "dev" */
		if (c != '\0')
			do  c = fubyte(ap++);
			while ((c != '/') && (c != '\0'));

		/* Gobble slashes between "dev" and "vms" */
		if (c != '\0')
			do c = fubyte(ap++);
			while ((c == '/') && (c != '\0'));

		/* Gobble "vms" */
		if (c != '\0')
			do c = fubyte(ap++);
			while ((c != '/') && (c != '\0'));
		/* Termination condition ap points to the unix style VMS name */
		ap--;
		DODEBUG(D_PIB,("vpopen/dr11: found beginnig of vms name\n"));
		if (c == 0) { /* empty vms name */
			u.u_error=EINVAL; dr11state=DR11FREE; break;
		}
		vmsp = vmsname;
		while (c != 0) {
			int inquote;
			cnamep = cname;

			/* Gobble Slashes */
			do c = fubyte(ap++); 
			while ((c == '/') && (c != '\0'));

			/* Gobble name (directory or file) */
			for (inquote=0,i=0; c!=0 && i<30;
			   cname[i]=c, c=fubyte(ap++), i++) {
				if (inquote) {
					if (c=='"') inquote=0;
					continue;
				} else if (c=='"') inquote=1;
				else if (c=='/' || c=='.' || c==':') break;
				else if ((c>='a' && c<='z') ||
					 (c>='A' && c<='Z') ||
					 (c>='0' && c<='9') ||
					 (c=='$' || c=='_') ) continue;
				else { /* name contained a non VMS char */
					u.u_error = EINVAL;
					dr11state  = DR11FREE;
					return;
				}
			}
			cname[i] = 0;
			if (c == '/') { /* cname contains a directory */
				if (!haveDev++) { /* Add VMS default device */
					for (cnamep=VMSDEVNAME; *cnamep != 0;)
						*vmsp++ = *cnamep++;
				}
				if (!haveBracket++) *vmsp++ = '[';
				else                *vmsp++ = '.';
				for (cnamep=cname; *cnamep ;) *vmsp++ = *cnamep++;
			} else if (c == ':') {
				if (!haveDev) { /* cname contains a node or device name */
					cname[i++] = c; c = fubyte(ap);
					if ( c == ':') { /* node name */
						cname[i++] = c;
						ap++;
					} else haveDev++; /* dev name */
					cname[i] = 0;
					for(cnamep=cname; *cnamep;) *vmsp++ = *cnamep++;

					/* Gobble Slashes */
					do c = fubyte(ap++); 
					while ((c == '/') && (c != '\0'));
					ap--;
				} else { /* name contained a non VMS char */
					u.u_error = EINVAL;
					dr11state  = DR11FREE;
					break;
				}
			} else if (c == '.'  ||  c == 0) {
				if (!haveDev++) {
					/* Add VMS default device */
					for (cnamep=VMSDEVNAME; *cnamep != 0;) *vmsp++ = *cnamep++;
					*vmsp++ = '[';
				}
				/* cname contains a file name. "." begins an extension */
				*vmsp++ = ']';
				for (cnamep=cname; *cnamep ;)
					*vmsp++ = *cnamep++;
				*vmsp++ = '.';
				break;
			} else { /* name contained a non VMS char */
				u.u_error = EINVAL;
				dr11state  = DR11FREE;
				break;
			}
		}
		/* add the extension */
		for (i=0; c != 0  &&  i < 3; i++) {
			c = fubyte(ap++);
			if (c == '/'  ||  c == '.') break;
			*vmsp++ = c;
		}
		*vmsp++ = 0;
		DODEBUG(D_PIB,("vpopen/dr11: vms file name = %s\n",vmsname));
		/* Send file name to VMS */
		setSeg(SSEG1_VA, dr11page);
		/* Soft reset dr11 communication area */
		*dr11tout = 1;	 /* Request 8086 to go into ignore state */
		/* wait until it goes to two == 8086 ignore state */
		while (*dr11tout != 2) {
			relSeg(SSEG1_VA);
			sleep(&lbolt,PZERO);
			setSeg(SSEG1_VA,dr11page);
		}
		*dr11i68 = *dr11i80 = 0;
		*dr11o68 = *dr11o80 = 0;
		*dr11rtout = 0;
		*dr11tout = 0;	 /* Mark dr11 ready for use */
		if (dr11Wwait()) return;
		DODEBUG(D_PIB,("dr11 I/O flag = %o\n",flag));
		dest=dr11Obuff[*dr11o68];
		if (flag&FWRITE) {*dest++=DR11CW[0]; *dest++=DR11CW[1];}
		else             {*dest++=DR11CR[0]; *dest++=DR11CR[1];}
		dr11OUidx = 2;
		for (vmsp=vmsname; *vmsp ;dr11OUidx++) *dest++ = *vmsp++;
		for (; dr11OUidx < DR11BSIZE ;dr11OUidx++) *dest++ = 0;
		/* update out pointers ... send block */
   		*dr11o68 = (*dr11o68+1) % MAXDR11BUFF; 
		dr11OUidx = 2;
		for (;;) {
			/* Gobble all input until a nak or ack is received */
			if (dr11Rwait()) return;
			dest=dr11Ibuff[*dr11i68];
			if (dest[0]==DR11VA[0] && dest[1]==DR11VA[1]) {
				DODEBUG(D_PIB,("vpopen/dr11: Found ACK\n"));
				break;
			}
			if (dest[0]==DR11VN[0] && dest[1]==DR11VN[1]) {
				DODEBUG(D_PIB,("vpopen/dr11: Found NCK\n"));
				u.u_error = EINVAL;
				dr11state  = DR11FREE;
				break;
			}
			*dr11i68  = (*dr11i68+1) % MAXDR11BUFF; 
			dr11IUidx = 2;
		}
		*dr11i68  = (*dr11i68+1) % MAXDR11BUFF; 
		dr11IUidx = 2;
		relSeg(SSEG1_VA); 
		DODEBUG(D_PIB,("vpopen/dr11: complete\n"));
		break;
		}
	default:
		u.u_error = EFAULT;
		break;
	}
	return u.u_error;
}

vpclose(dev, flag)
	dev_t	dev;
{
	register char *dest;
	unsigned long offset;
	short s;

	if (minor(dev) == VPLOADDEV) {
		DODEBUG(D_PIB,("8086 loaded\n"));
		setSeg(SSEG1_VA, vpMpage);
		vpintrflg  = (char *) (SSEG1_VA + VPINTER); *vpintrflg = 0;
		buffprint  = (char  *) (SSEG1_VA + VPPRINT); *buffprint = 0;
		relSeg(SSEG1_VA);
		/* Initialize DR11 communication pointers */
		offset =  (unsigned long)  (DR11BASE & 0xFFF);
		dr11Ibuff = (Tdr11buff *)  (SSEG1_VA + offset + DR11IBUFF);
		dr11Obuff = (Tdr11buff *)  (SSEG1_VA + offset + DR11OBUFF);
		dr11i68   = (short *)	  (SSEG1_VA + offset + DR11I68);
		dr11i80   = (short *)	  (SSEG1_VA + offset + DR11I80);
		dr11o68   = (short *)	  (SSEG1_VA + offset + DR11O68);
		dr11o80   = (short *)	  (SSEG1_VA + offset + DR11O80);
		dr11tout  = (char  *)	  (SSEG1_VA + offset + DR11RESET);
		dr11rtout = (char  *)	  (SSEG1_VA + offset + DR11RTOUT);
		dr11page  = (char  *)	  ((unsigned long) vpMmem + DR11BASE);
		setSeg(SSEG1_VA, dr11page);
		*dr11i68 = *dr11i80 = 0;
		*dr11o68 = *dr11o80 = 0;
		*dr11tout = 0; *dr11rtout = 0;
		relSeg(SSEG1_VA); 
		/* Start vp seg address & go command */
		sPset(vpCSegReg, (short) (((int) vpMmem) >> 16)); 
		sPset(vpDSegReg, (short) (((int) vpMmem) >> 16)); 
		sPset(vpHmem,	(short) (((int) vgHardCopyMem) >> 16));
		sPset(vpStart, (short) 0x20); 
		vpstate   = RUNNING;
		dr11state = DR11FREE;
		return;
	}
	if (minor(dev) == VPVPLOT || minor(dev)==VPVPRINT) {
		hcmclose(dev,flag);
		vpstate = RUNNING;
		return;
	}
	if (minor(dev) == DR11DEV) {
		DODEBUG(D_PIB,("Close of dr11 dev. flag = %d\n",flag));
		setSeg(SSEG1_VA, dr11page);
		if (*dr11tout) {
			relSeg(SSEG1_VA); 
			u.u_error = EFAULT;
			dr11state = DR11FREE;
			return;
		}
		if (flag & FWRITE) {
			dest = &dr11Obuff[*dr11o68][dr11OUidx];
			/* Zero remaining part of last block */
			for (; dr11OUidx<DR11BSIZE; dr11OUidx++) *dest++=0;
			/* Send last block */
			dest-=DR11BSIZE;
			*dest++=DR11CL[0]; *dest++=DR11CL[1];
			*dr11o68  = (*dr11o68+1) % MAXDR11BUFF; 
			dr11OUidx = 2;
			/* Send EOF block */
			if (dr11Wwait()) return;
			dest=dr11Obuff[*dr11o68];
			*dest++=DR11CE[0]; *dest++=DR11CE[1];
			for (dr11OUidx=DR11BSIZE-2; --dr11OUidx>=0; ) *dest++=0;
			*dr11o68  = (*dr11o68+1) % MAXDR11BUFF; 
			dr11OUidx = 2;
			/* wait for ACK/NAK */
			if (dr11Rwait()) return;
			dest=dr11Ibuff[*dr11i68];
			if (! (*dest++==DR11VA[0] && *dest++==DR11VA[1]))
				u.u_error = EIO;
			*dr11i68  = (*dr11i68+1) % MAXDR11BUFF; 
			dr11IUidx = 2;
		} else { /* (flag & FREAD ) */
			DODEBUG(D_PIB,("Close read of dr11 dev.\n"));
			if (! (dr11state == DR11REOF  ||  dr11state == DR11REOF2)) {
				/* 
				* Enter gobble state. The rest of the work is done by
				* the interrupt procedure.
				*/
				u.u_error = EIO;
				dr11state = DR11GOBBLE;
				/* set read request/timout */
				*dr11rtout = 1;
				relSeg(SSEG1_VA); 
				return;
			}
			if (dr11Wwait()) return;
			/* Send ACK */
			dest=dr11Obuff[*dr11o68];
			*dest++=DR11CA[0]; *dest++=DR11CA[1];
			for (dr11OUidx=DR11BSIZE-2; --dr11OUidx>=0; ) *dest++=0;
			*dr11o68  = (*dr11o68+1) % MAXDR11BUFF; 
			dr11OUidx = 2;
		}
		relSeg(SSEG1_VA); 
		dr11state = DR11FREE;
		return;
	} 
}


int vpLMem;
static int   mload;
static char *vpdest;
static struct vpl {
	int Msize,Mlow,Vsize,Vlow;
} vpl;

vpwrite(dev, uio)
	dev_t	dev;
	register struct	uio	*uio;
{
	switch (minor(dev)) {
	case VPLOADDEV: {
		/* Limitations:
	 	*   a) on the first write the entire header
	 	*      must be loaded at one time 
	 	*   b) writes must be diviable by 4
	 	*/
		register unsigned int icnt;
		register int i;
		register int j;
		register char *Mhigh;
		register char *Vhigh;
		if (vpstate == FIRSTLOAD) {
			DODEBUG(D_PIB,("vpwrite: loading....\n"));
			if (uio->uio_resid<16) return(EIO);
			vpstate = LOADING;
			uiomove((caddr_t)&vpl,sizeof(vpl),UIO_WRITE,uio);
			vpl.Mlow   = (int) (vpMmem + vpl.Mlow);
			vpl.Vlow   = (int) (VPBASE + vpl.Vlow);
			if (vpl.Msize != 0) { vpdest = (char *) vpl.Mlow; mload = 1; }
			else            { vpdest = (char *) vpl.Vlow; mload = 0; }
			DODEBUG(D_PIB,("vpl.Msize=%x, vpl.Mlow=%x, vpl.Vsize=%x, vpl.Vlow=%x\n",
		        	(int)vpl.Msize, (int)vpl.Mlow, (int)vpl.Vsize, (int)vpl.Vlow));
		}
		Mhigh = (char *) (vpl.Mlow + vpl.Msize);
		Vhigh = (char *) (vpl.Vlow + vpl.Vsize);
		while (uio->uio_resid > 0) {
			if (mload) { /* loading multibus memory */
				if (vpdest >= Mhigh) { /* Only allow one vg load on multi-bus segment */
					vpLMem = 1;
					mload  = 0;
					vpdest = (char *) vpl.Vlow;
					continue;
	        		} else {
					if ((icnt=Mhigh-vpdest)>uio->uio_resid)
						icnt = uio->uio_resid;
				}
				if (vpLMem) { /* Only allow one vg load on multi-bus segment */
					vpdest += icnt;
					uioflush(icnt,uio);
					continue;
				}
			} else { /* Loading the board */
				if (vpdest >= Vhigh) {
					DODEBUG(D_PIB,("Memory overflow during load\n"));
					return(ENOMEM);
				} else if ((icnt=Vhigh-vpdest)>uio->uio_resid)
					icnt = uio->uio_resid;
			}
			j=pagesize-(pagemask&(int)vpdest);
			if (icnt>j) icnt=j;
			setSeg(SSEG1_VA, vpdest);
			uiomove(SSEG1_VA+(pagemask&(int)vpdest),icnt,UIO_WRITE,uio);
			relSeg(SSEG1_VA);
			vpdest+=icnt;
		}
		return(0);
	}
	case VPVPLOT: case VPVPRINT:		/* Versatec */
		return(hcmwrite(dev,uio));

	case DR11DEV: {
		register unsigned int   count;
		DODEBUG(D_PIB,("Write to dr11 dev\n"));
		setSeg(SSEG1_VA, dr11page);
		while ((count=uio->uio_resid)>0) {
			register char *dest;
			for (; dest=dr11Obuff[*dr11o68], dr11OUidx>=DR11BSIZE; dr11OUidx=2) {
				/* current buffer full, not last block */
				*dest++=DR11CB[0]; *dest++=DR11CB[1];
				*dr11o68  = (*dr11o68+1) % MAXDR11BUFF; 
			}
			if (dr11Wwait()) break;
			DODEBUG(D_PIB,
			    ("vpwrite/dr11: Start copying data to buffer\n"));
			if (count>(DR11BSIZE-dr11OUidx)) count=DR11BSIZE-dr11OUidx;
			uiomove(dest+dr11OUidx,count,UIO_WRITE,uio);
			dr11OUidx+=count;
		}
		relSeg(SSEG1_VA);
		DODEBUG(D_PIB,("vpwrite/dr11: write complete\n"));
		break;
	}
	default:
		u.u_error = ENODEV;
	}
	return u.u_error;
}


vpread(dev, uio)
	dev_t	dev;
	register struct	uio	*uio;
{
	register unsigned int count;

	switch (minor(dev)) {
	case DR11DEV:
		DODEBUG(D_PIB,("vpread/dr11 entered\n"));
		if (dr11state == DR11REOF) {
			DODEBUG(D_PIB,("...on EOF condition\n"));
			if (dr11state==DR11REOF2) uio->uio_resid++;
			dr11state = DR11REOF2;
			break;
		}
		setSeg(SSEG1_VA, dr11page);
		while ((count=uio->uio_resid)>0) {
			register char *src=dr11Ibuff[*dr11i68];
			register int n=DR11BSIZE-dr11IUidx;
			if (dr11Rwait()) return;
			DODEBUG(D_PIB,("vpread/dr11 read data\n"));
			if (src[0]==DR11VE[0] && src[1]==DR11VE[1]) {
				DODEBUG(D_PIB, ("...EOF encountered\n"));
				*dr11i68  = (*dr11i68+1) % MAXDR11BUFF;
				relSeg(SSEG1_VA);
				dr11IUidx = 2;
				dr11state = DR11REOF;
				break;
			}
			if (count>n) count=n;
			{/* a 0 byte within a packet marks the end of data.
			 /* this means binary files won't work.
			 /* a byte count field should be added to the packet.
			 */
				register char *p=src+dr11IUidx;
				while (--n>=0) if (*p++==0) {
					count=((--p)-src)-dr11IUidx; break;
				}
			}
			uiomove(src+dr11IUidx,count,UIO_READ,uio);
			dr11IUidx+=count;
			if (dr11IUidx>=DR11BSIZE || n>=0) {/* n>=0 for 0 byte */
				DODEBUG(D_PIB,
				    ("vpread/dr11 block exhausted inc %d\n",
				    *dr11i68));
				/* current block is finished */
				*dr11i68  = (*dr11i68+1) % MAXDR11BUFF;
				dr11IUidx = 2;
			}
		}
		relSeg(SSEG1_VA);
		break;

	case VPVPLOT: case VPVPRINT:		/* versatec */
		return(hcmread(dev,uio));

	default:
		u.u_error = ENODEV;
	}
	return u.u_error;
}


vpioctl(dev, cmd, addr, flags)
dev_t   dev;
caddr_t addr;
{
	DODEBUG(D_PIB,("vpioctl(0x%x 0x%x 0x%x 0x%x)\n",dev,cmd,addr,flags));
	if (minor(dev) == VPVPLOT || minor(dev)==VPVPRINT) {
		switch (cmd) {
		case BUFFFLUSH : buffflush(); break;
		case BUFFPRINT :
			buffflush();
			setSeg(SSEG1_VA, vpMpage);
			*buffprint = 1+(minor(dev)==VPVPRINT);
			relSeg(SSEG1_VA);
			break;
		}
	}
	return u.u_error;
}

buffflush()
{
	short s;

	setSeg(SSEG1_VA, vpMpage);
	s = spl1();
	while (*buffprint != 0) {/* buffer is not finished printing */
		DODEBUG(D_PIB,("Printer buffer full. Sleep\n"));
		relSeg(SSEG1_VA);
		sleep((caddr_t) buffprint,VPSLEEP);
		setSeg(SSEG1_VA, vpMpage);
	}
	splx(s);
}

dr11Rwait()
{
	register short s;
	register short setrout;

	/* Wait for input */
	/* IMPORTANT: A setSeg of dr11page must happen before this procedure */
	s = spl1();
	setrout = 0;
	while (*dr11i68 == *dr11i80)  {/* While empty */
		DODEBUG(D_PIB,("dr11 No input. Sleep\n"));
		if (*dr11tout) break;
		if (setrout == 0) { setrout = 1; *dr11rtout = 1; }
		relSeg(SSEG1_VA);
		sleep((caddr_t) dr11Ibuff, VPSLEEP);
		setSeg(SSEG1_VA, dr11page);
	}
	splx(s);
	if (*dr11tout) {/* timeout error */
		relSeg(SSEG1_VA); dr11state=DR11FREE; u.u_error=EFAULT;
		return(-1);
	}
	return(0);
}

dr11Wwait()
{
	short s;

	/* Wait for for free buffer in ouput queue */
	/* IMPORTANT -- A setSeg of dr11page must happen before this procedure */
	s = spl1();
	while ((*dr11o68+1)%MAXDR11BUFF == *dr11o80) {/* while full */
		DODEBUG(D_PIB,("dr11 output buffer full. Sleep\n"));
		if (*dr11tout) break;
		relSeg(SSEG1_VA); 
		sleep((caddr_t) dr11Obuff,VPSLEEP);
		setSeg(SSEG1_VA, dr11page);
	}
	splx(s);
	if (*dr11tout) {/* timeout error */
		relSeg(SSEG1_VA); dr11state=DR11FREE; u.u_error=EFAULT;
		return(-1);
	}
	return(0);
}
#endif NVP
