static char rcsid[] = "$Header: vpb.c,v 820.1 86/12/04 19:57:08 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.				*
*									*
************************************************************************/

/*
 * Notes:
 *	vpb.c is the high-level interface to the IBM pib.
 *	It talks to these files:
 *		vpload			Board load file
 *		dmgrload		Display manager load file
 *		vpcom			"Communications" (versatec plotter)
 *		vplot			same as vpcom
 *		vprint			versatec as a printer
 *		RSCS*			RSCS communications link
 *	I don't understand why the display manager load file is here;
 *	it would be better implemented as a segment mapped in via the
 *	mem file.
 *	"Real I/O" is handled by a renegade cdevsw in the bdt structure,
 *	"doio".  The routines so referenced are found in pib.c.
 *	"Load/diagnostic I/O" is implemented by the routines in this file.
 *
 *	This version comes from 5.1(7/6/83).  I erroneously
 *	obtained an old version and modified it extensively.
 *	When I realized my error, rather than starting over
 *	I riffled into this version the changes that had
 *	been made between 5.1 and 6.1(9/29/83), the most
 *	recent "current" version.
 *
 *	Written by Carol Farlow.  Extensively altered, improved, modified,
 *	and hacked by Mark Seiden, Strafford Wentworth, John F. Reiser,
 *	James Markevitch and Steven Sargent (this last to convert to 4.1c).
 *
 * Steven Sargent  2-3-84
 */

#include "vg.h"
#include "pib.h"
#if NPIB > 0 || NVG > 0

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/proc.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../s32/debug.h"

#include "../s32dev/useful.h"
#define NBD 1		/* Here don't care about exact dimension 	*/
#include "../s32dev/vpb.h"
#include "../s32dev/regmuck.h"
#define REGSET(x)\
	regmuck(bdtp->x.op,bdtp->x.address+bp->boardMba,&(bdtp->x.value))
#define REGCLR(x)\
	regmuck((bdtp->x.op)^1,bdtp->x.address+bp->boardMba,&(bdtp->x.value))
#include "../machine/cpu.h"
#include "../h/user.h"

/* The following integer defines the Multibus segment containing the
/* display manager code. It should be set by an ioctl after the display mgr
/* is loaded, but for now we provide a field to be set from adb.
*/
dispmgrseg = {0};

/* This table gives printable names for error codes that get logged.	*/
static char errname[][9] = {"diagmode",	/* E_DIAGMODE			*/
		     "wonthalt",	/* E_NOHALT			*/
		     "wontrun",		/* E_NORUN			*/
		     "noprivar",	/* E_ZERPRV			*/
		     "badcomm",		/* E_BADCOM			*/
		     "nobuffer"};	/* E_NOBUF			*/
/* This temporary subroutine logs errors on the console. Some more permanent
   error record is desirable.
 */

errlog(bdtp,bp,errcode,opcode)
	struct bdt *bdtp;
	struct bdata *bp;
	short errcode, opcode;
{
	printf("valid peripheral board(%s) at address 0x%X, ",
		&bdtp->type,bp->boardMba);
	if (errcode) printf("error %s, operation 0x%X.\n",
		errname[-(errcode+1)],opcode);
	else printf("%s \n", (opcode&CLOSE)? "running": "halted");
}


/* This subroutine validates that the memory starting at addr and running
 * for count bytes is legitimately a part of board(bdtp,bp).
 * sbs: failure of this function seems a panic-worthy condition
 */
byte *
valaddr(bdtp,bp,addr,count)
	struct bdt *bdtp;
	struct bdata *bp;
	int addr;
	int count;
{
	DODEBUG(D_PIB,("VALADDR: bdtp 0x%X, bp 0x%X, addr 0x%X, count 0x%X\n",
		bdtp,bp,addr,count));
	return(
	((0 <= addr) && (addr+count <= bdtp->onbdend))
		? (bp->boardMba) 
			? addr+bp->boardMba
			: (byte *)E_BADARG
		: (((bdtp->offbdbase) <= addr) && 
		   (addr+count <= (bdtp->offbdbase+(bdtp->Mbpgs<<PGSHFT)))
		  ) 	? addr - bdtp->offbdbase + bp->privMba
			: (byte *)E_BADARG
	);
}

/* This routine halts a board described by (bdtp,bp). Failure to halt is
 * logged via errlog.
 */

halt(bdtp,bp,opcode) /* Here to halt an 8086			    */
	struct bdt *bdtp;
	struct bdata *bp;
	int opcode;
{
	int retval;
	DODEBUG(D_PIB,("HALT: bdtp 0x%X, bp 0x%X, opcode 0x%X\n",bdtp,bp,opcode));
	retval= E_NOHALT;
	if (bdtp->watch && (bp->flag&WATCHOUT)) {
		/*cantime(bdtp->watch,bp,600);*/
		/*??Cancel timer */
		bp->flag&=~WATCHOUT;
	};
	if (regmuck(bdtp->halt.op,bdtp->halt.address+bp->boardMba,&bdtp->halt.value))
		if (PROBE(bp->boardMba)) {
			REGSET(redled);		/* Turn on red led */
			REGCLR(greenled);	/* Turn off green led */
			bp->flag&=~RUNNING;
			retval=0;
		} else bp->flag|=BAD;
	errlog(bdtp, bp, retval, opcode);
	return(retval);
}


/* Cause the board described by (bdtp,bp) to begin executing.  Log failures
 * via errlog.
 */
run(bdtp, bp, opcode)	/*Here to restart 8086 */
	struct bdt *bdtp;
	struct bdata *bp;
	int opcode;
{
	int retval;
	DODEBUG(D_PIB,("RUN: bdtp 0x%X, bp 0x%X, opcode 0x%X\n",bdtp,bp,opcode));
	retval= E_NORUN;
	REGSET(greenled);
	REGCLR(redled);
	if (regmuck(bdtp->run.op,bp->boardMba+bdtp->run.address,&bdtp->run.value))
		if (!PROBE(bp->boardMba)) {
			retval=0;
			bp->flag|=RUNNING;
			if (bdtp->watch)
				timeout(bdtp->watch, bp, 600);/*10sec.watchdog*/
		} else {
			REGSET(redled);
			REGCLR(greenled);
			bp->flag |= BAD;
		};
	errlog(bdtp, bp, retval, opcode);
	return(retval);
}


/* Mark a board no longer active, and handle clearing of shared board status
 * flags.
 */
clrsbact(bdtp,sbp)
	struct bdt *bdtp;
	struct bdata *sbp;
{
	register struct bdata *bp;
	register struct bdata *blast = &sbp[bdtp->sharemask];

	DODEBUG(D_PIB,("CLRSBACT: bdtp 0x%X, sbp 0x%X\n", bdtp, sbp));
	if (sbp >= 0) {
		for (bp = sbp; bp <= blast; bp++)
			if(bp->mdopen)		/* Something still open */
				return;
		sbp->flag &= ~ACTIVE;   /* This close got last active device  */
	}
}

#define DC 0
#define SBF 0X1
#define SBO 0X100
#define MBF 0X10000
#define MBO 0x1000000
#define VERROR 0X0000FFFF
#define VIMPOSS 0XFFFF0000
#define V(b7,b6,b5,b4,b3,b2,b1,b0)\
b0|(b1<<1)|(b2<<2)|(b3<<3)|(b4<<4)|(b5<<5)|(b6<<6)|(b7<<7)

static bit32 vtable[16]={						/*
    OPCODE	  BOARD STATUS BITS	SHARED CODE STATUS BITS
   S L     O	B    A    R    L       B    A    R    L
   H D     P	A    C    U    O       A    C    U    O
   A I     E	D    T    N    A       D    T    N    A
   R A     R	     I    N    D	    I    N    D
   E G     A	     V    I    I	    V    I    I
   D O     T	     E    N    A	    E    N    A
   C P     I		  G    G		 G    G
   D       O
	   N
________________________________________________________________________*/
/*<REAL> open */V( SBF, DC,  SBO, SBF,    MBF, DC,  SBO, SBF ),
/* 0 0   close*/V( SBF, MBO, MBO, MBF,    MBF, DC,  MBO, MBF ),
/* 0 0   io   */V( SBF, MBO, MBO, MBF,    MBF, DC,  MBO, MBF ),
/* 0 0   cntl */V( SBF, MBO, MBO, MBF,    MBF, DC,  MBO, MBF ),
/*<LD/DG>open */V( SBF, SBF, DC,  SBF,    SBF, DC,  DC,  SBF ),
/* 0 1   close*/V( SBF, MBO, DC,  MBO,    MBF, DC,  MBO, MBF ),
/* 0 1   io   */V( SBF, MBO, DC,  MBO,    MBF, DC,  MBO, MBF ),
/* 0 1   cntl */V( SBF, MBO, DC,  MBO,    MBF, DC,  MBO, MBF ),
/*  SHAREDCD  */
/*<REAL> open */		    VERROR,
/* 1 0   xxxx */	VIMPOSS, VIMPOSS, VIMPOSS,
/*<LOAD> open */V( MBF, SBF, DC,  SBF,    DC,  DC,  DC,  DC  ),
/* 1 1   close*/V( MBF, MBF, MBF, MBO,    DC,  DC,  DC,  DC  ),
/* 1 1   io   */V( MBF, MBF, MBF, MBO,    DC,  DC,  DC,  DC  ),
/* 1 1   cntl */		    VIMPOSS};


/*  SBO=should be on;  SBF=should be off;  DC=don't care; 
	VERROR=operation invalid;    VIMPOSS=consistency error		*/

/* The following table describes the size of arguments to various ioctls*/
static short arglen[MAXIOCTL+1] = {
	6,	/* START */
	0,	/* SETADDR */
	0,	/* SETBDT */
	1,	/* SETINTR */
	4,	/* GETBDT */
	0,	/* RSCS	N.B. CHANGE THIS TO SIZEOF EXCHLIST*/
	0,	/* VRSFMFD */
	0,	/* VRSPLOT */
	0,	/* VRSPRINT */
	0,	/* VRSLTR */
	0,	/* VRSBLOCKPLOT */
	0,	/* VRSFLUSH */
	0	/* RSCSSETN */
};


/* vpbcopen, vpbcread, vpbcwrit, vpbcclos, vpbccntl are in cdevsw.
 * To do: should this driver have a SELECT function?
 */
vpbcopen(d, rw) 
	dev_t d;
	register rw;
{
	if (rw&FWRITE)
		rw = VPBWRITE;
	else if (rw&FREAD)
		rw = VPBREAD;
	return(vpbdo(d, rw, OPEN, 0));
}


vpbcread(d, uio)
	dev_t d;
	struct uio *uio;
{
	return(vpbdo(d, VPBREAD, IO, uio));
}


vpbcwrit(d, uio)
	dev_t d;
	struct uio *uio;
{
	if (minor(d)==31) {/* vpload */
		register struct bdata *bp=
			&(((struct bdt*)cdevsw[major(d)].d_ttys)->b[0]);
		while (uio->uio_resid>0) {
			register int k=uio->uio_offset;
			if (k<0x10000) k+=(int)(bp->boardMba);  /* onboard */
			else k+=(int)(bp->privMba)-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);
	}
	return(vpbdo(d, VPBWRITE, IO, uio));
}


vpbcclos(d, rw)
	dev_t	d;
	register rw;
{
	if (minor(d)==31) {/* vpload */
		extern struct bdt pibdt,vgbdt;
		register struct bdt *bdtp=(struct bdt*)cdevsw[major(d)].d_ttys;
		register struct bdata *bp= &(bdtp->b[0]);
		sPset(bp->boardMba+bdtp->segregad[0],
			((int)pibdt.b[0].privMba)>>16);
		sPset(bp->boardMba+bdtp->segregad[1],
			((int)vgbdt.b[0].privMba)>>16);
		run(bdtp,bp,CLOSE);
	}
	if (rw & FWRITE)
		rw = VPBWRITE;
	else if (rw & FREAD)
		rw = VPBREAD;
	return(!(u.u_error = vpbdo(d, rw, CLOSE, 0)));
}


vpbccntl(d, cmd, arg, flag)
	dev_t	d;
	int	cmd,
		flag;
	byte	*arg;
{
	return(((cmd&0XFF) <= MAXIOCTL)
			? vpbdo(d, cmd, VPBCONTROL, arg)
			: /*EFAULT*/ EINVAL);
}

extern copybuf();
extern struct bdt vgbdt,
		  pibdt;
extern boolean regmuck();


/* vpbdo(d, rw, opcode, uio)
 *	Common code for I/O operations: at this writing, we really only
 *	talk to IBM pibs and the display manager load file,
 *	although clearly something more grandiose was
 *	planned...  On device "d", do the operation specified by
 *	"opcode", possibly copying in/out through "uio."  On real I/O
 *	the "rw" is passed on through to the board service routine
 *	(cf. pib.c); otherwise (load/diagnostic) do the services
 *	requested ourself.
 *	--sbs 1-20-84
 */
vpbdo(d, rw, opcode, uio)
	dev_t d;	/*UNIX device number*/
	int rw;		/*1 = write, 2 = read, * = update or ioctl code*/
	int opcode;	/*OPEN, CLOSE, VPBCONTROL...*/
	struct uio *uio;
{
	short	bdnum,
		devnum,
		sbd;
	int	retval = 0;
	register struct bdt	*bdtp;
	register struct bdata	*bp;
	devmask	mask;

	/* used to validate board accesses
	 */
	union {
		struct {
			byte	sumstat,
				sumbar1,
				sumstat2,
				sumbar2;
		} b;
		struct {
			bit16	mbo_f,
				sbo_f;
		} w;
		bit32	vlong;
	} reg;

	DODEBUG(D_PIB,("VPBDO: d=0x%X rw=0x%X opcode=0x%X uio=0x%X\n",
		d, rw, opcode, uio));

	bdtp = (struct bdt *)cdevsw[major(d)].d_ttys;
	d = minor(d);

	/* Form and validate bdnum, devnum and mask. */
	if ((bdnum=d>>bdtp->minorbits)>=bdtp->maxboards ||
 		       !((mask=(1<<(devnum=d&bdtp->minormask)))&bdtp->mdlegal)) 
		panic("vpbdo1");

	/* Establish the board data pointer */
	bp= &bdtp->b[bdnum];

	/* Now form a summary status flag, whose high nybble contains this board's
 	* RELEBITS and whose low nybble has the shared code "board" RELEBITS.
 	* Use opcode (augmented with a properly set LOADIAGOP and SHAREDCD bit)
 	* to index the table to validate the status.  Table entries are 4 bytes long
 	* with "must be on", "must be off", "should be on" and
 	* "should be off" masks.  Compare against a value containing
 	*   (summary flag, ~(summary flag), 
 	*	   summary flag, ~(summary flag))&table entry.
 	* If the anded value does not equal table entry, there is an error. If the 
 	* inequality is in the high order 2 bytes, the error is an inconsistency
 	* requiring "panic", else the error simply results in a bad return 
 	* code for the caller (i.e. operation is inappropriate at this time.)
 	*/
	if (sbd=bdtp->sharemask) {
		reg.b.sumstat=bdtp->b[sbd=bdnum&~sbd].flag&RELEBITS;
		if (bdnum==sbd) opcode|=SHAREDCD;
	} else {
		sbd= -1;
		reg.b.sumstat=FAKESBFL;
	};
	reg.b.sumbar1=reg.b.sumbar2=
		~(reg.b.sumstat2=reg.b.sumstat|=(bp->flag&RELEBITS)<<4);
	if (devnum==bdtp->minormask)
		opcode|=LDIAGOP;

	DODEBUG(D_PIB,(
		"VPBDO2: bdnum=0x%X devnum=0x%X sbd=0x%X mask=0x%X opcode=0x%X\n",
		bdnum, devnum, sbd, mask, opcode));
	DODEBUG(D_PIB,("VPBDO3: reg.vlong=0x%X bdtp=0x%X bp=0x%X\n",
		reg.vlong,bdtp,bp));

	/* Now validate */
	if ((reg.vlong&vtable[opcode&0XFF])!=vtable[opcode&0XFF]) {
		/* Validity check failed */
		DODEBUG(D_PIB,("VPBDO4: vtable[0x%X]=0x%X\n",
			opcode&0xFF, vtable[opcode&0XFF]));
		if ((reg.vlong&vtable[opcode&0XFF]&0XFFFF0000)!=
				vtable[opcode&0XFF]&0XFFFF0000)
			panic("vpbdo2"); /* error was inconsistency */
		else
			return EINVAL;	/* error was bad operation */
	}
	if ((opcode&LDIAGOP)==0) {
		switch (opcode&OPMASK) {
		case OPEN:
			/* File is open -- go away.  Note that this is
			 * insufficient for versadreks because there are
			 * 2 files talking to them!  (resolved in pibopen)
			 */
			if (bp->mdopen & mask)
				retval = EIO;
			break;
		case CLOSE:
			if ((bp->mdopen &= ~mask)==0) 
				bp->flag &= ~ACTIVE;
			clrsbact(bdtp,&bdtp->b[sbd]);
			break;
		}
		if (retval == 0 && bdtp->doio[opcode])
			retval = (*bdtp->doio[opcode])(devnum, bp, rw, uio);
		if (((opcode&OPMASK) == OPEN) && retval == 0) {
			bp->flag |= ACTIVE;
			bp->mdopen |= mask;
			if (sbd >= 0) 
				bdtp->b[sbd].flag |= ACTIVE;
		}
	} else switch (opcode&OPMASK) {		/* diagnostic operation */
		char	c;
		int	i,x;
		byte	temp[5],
			segval,
			*vaddr;

	case OPEN:
		if (rw==VPBWRITE) {/* Loading. */
			if (opcode&SHAREDCD) {

				struct bdata *tbp;

				/*code size=0 */
				bp->privMba=bp->boardMba;
				for (tbp=
					&bdtp->b[bdnum|bdtp->sharemask];
				     tbp>bp;
				     tbp--)
					if (tbp->boardMba &&
					    ((tbp->flag&BAD)==0))
						halt(bdtp,tbp,opcode);
			} else if ((retval = halt(bdtp, bp, opcode)) == 0) {
				pibwake(bp);
				if (sbd >= 0) 
					bdtp->b[sbd].flag |= ACTIVE;
				bp->mdopen |= mask;
			}
		}
		if (!retval) 
			bp->flag |= (LOADDIAG|ACTIVE);
		break;

	case CLOSE:
		bp->flag &= ~(LOADDIAG|ACTIVE);
		/* Mark doneload or diag in prog*/
		if ((opcode&SHAREDCD) == 0) {/* Real board */
			bp->mdopen &= ~mask;
			clrsbact(bdtp, &bdtp->b[sbd]);
		} else {
			short t; 
			struct bdata *tbp;
			byte *base;

			/* Compute private area length */
			t=bdtp->Mbpgs-btoc(bp->privMba-bp->boardMba)
				/(bdtp->sharemask);
			if (t) {
				/* for all sharers */
				for (tbp=bp+1, base=(byte *)
				   (((int)bp->privMba+PGMASK)&PGMASK);
				     tbp<=bp+bdtp->sharemask;
				     tbp++, base+=t<<PGSHFT)
					tbp->privMba=base;
				bp->flag|=RUNNING;
			} else {
				errlog(bdtp, bp, /*retval=*/E_ZERPRV, opcode);
				retval = EIO;
			}
		}
		break;
	case IO:
		x=(int)(bp->curaddr);
		if (7==d) x=uio->uio_offset; /* dmgrload */
		if ((i = (int)valaddr(bdtp, bp, x, uio->uio_resid)) > 0) {
			DODEBUG(D_PIB,("VPBDO/%s: target=0x%X(0x%X)\n",
				(rw==VPBWRITE)?"W":"R",i,x));
			retval = pibcp(i, uio->uio_resid, rw, uio);
		} else retval = EFAULT;
		break;
	case VPBCONTROL:
		switch (rw & 0XFF) {
		case 0:	/* START */
			panic("VPBCONTROL START");
			break;
		
		case 1: /* SETADDR */
			bp->curaddr= (byte *)*(long *)uio;
			break;

		/* case 2: SETBDT obsolete */
		/* case 3:  SETINTR obsolete */
		/* case 4: GETBDT obsolete */
		default:
			retval= EINVAL;
			break;
		}

	}
	DODEBUG(D_PIB,("VPBDO5: retval=0x%X\n\n",retval));
	return(retval);
}

#endif NPIB > 0 || NVG > 0
