#include "drc.h"
#if NDRC > 0
/*
 *  Cidmac Robot  FIFO driver
 *
 *  After doing a successful open on this device, the user process
 *  must fill in the "drcROBOT" structure (in drc.h) and "write"
 *  it to this device. The structure includes Read buffer, count,
 *  write buffer (the count is the first word) and the address of
 *  a user interrupt routine which gets executed on a Dr11-c interrupt
 *  after the buffer is read from the Dr11-c and placed directly
 *  in user memory. After executing the user routine, the data in
 *  the output buffer is written back to the Dr11-c. Doing ANY syscalls
 *  from the user interrupt routine IS PROHIBITED!!! and will crash
 *  the system with a "CHM? ERROR".  Also the user interrupt routine
 *  has full R/W access to kernel memory and any traps, aborts, or
 *  other faults will result in a system crash.  Care should be
 *  taken not to write to any address with bit 31 set (80XXXXXX)
 *  since this is the kernel. (The stack is here though, so don't
 *  put very much stuff there). No syscalls are used to R/W this
 *  device once the initial write is done since they are too slow.
 *  The kernel interrupt routine handles it all from previously
 *  specified parameters.
 *  On any Dr11-c error, a uprintf() is issued to the controlling
 *  tty and the console and the user process is sent a SIGHUP signal
 *  since there is no means of returning an error.
 *
 *  Warning: This driver needs the file close code in sys1.c/exit()
 *  moved up before the code to release memory.  Also many unorthodox
 *  things are used here to gain speed for a realtime environment
 *  like running a user routine in kernel mode on the intstk and
 *  skipping protection, etc, etc.  Anybody who uses this driver
 *  must be checked out in ROOT privelidges and system startup/
 *  shutdown/realtime procedures.  This driver must take an interrupt
 *  every 7 milliseconds (1/2 tick) and let the user routine run for
 *  5 milliseconds.  This must be used in a single user enivornment.
 *  Also any process with this device open, must NOT FORK(), VFORK(),
 *  EXEC(), or do anything with raw I/O or which expands the data or
 *  stack regions (like break() or growing the stack). Also the "open"
 *  of this driver must be followed by a "write(fd, &drcROBOT, sizeof drcROBOT)"
 *  to load initial parameters (should have used ioctl instead).
 *  None of the parameters is checked (except for the write being done)
 *  for validity.  Failure to heed the warnings will result in system
 *  crashes.  Good luck.
 *  --ghg 1/14/82.
 *
 *	added minor device number for stanford arm 30 Oct 84 BUCK
 */
int drcdebug = 0;

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/tty.h"
#include "../h/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/conf.h"
#include "../h/mtpr.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"

#include "../h/drc.h"

#define PUMA
#ifdef PUMA
#define	ROBOT		0	/* minor device of Puma robot */
#endif

#ifdef STAN
#define	ROBOT		1	/* minor device of Stanford robot */
#endif

#define	DRC_DELAY	50000	/* spin loop timeout */

#define	DRC_CSR0	010000	/* bit 0 (to user dev) */
#define	DRC_CSR1	020000	/* bit 1 (to user dev) */
#define	DRC_INT		01	/* INTER other cpu */
#define	DRC_IEA		0100	/* INTER Enable A */
#define	DRC_REQA	040000	/* REQ A line from user dev */
#define	DRC_REQB	0100000	/* REQ B line from user dev */
#define	FIFO_EMPTY	0200


struct drc_softc {
	int	sc_openf;
	struct	tty *sc_ttyp;
	int	sc_nice;
	struct	proc *sc_procp;	/* proc with this dev open */
	caddr_t	sc_rbuf;	/* read buffer address */
	int	sc_rcount;	/* byte count */
	caddr_t	sc_wbuf;	/* write buffer address */
	int	sc_P0BR;	/* This proc's P0BR, P0LR, P1BR, P1LR */
	int	sc_P0LR;
	int	sc_P1BR;
	int	sc_P1LR;
	int	sc_setup;	/* nz of setup complete */
	int	(*sc_routine)();/* address of user interrupt routine */
	int	(*sc_routine2)();/* address of user interrupt routine2 */
	int	sc_vslocked;	/* number of bytes locked down */
} drc_softc[NDRC];

struct drcdevice {
	u_short	drccsr;
	u_short	drcbuf;
};

int	drcprobe(), drcattach(), drcintr();
struct	uba_device *drcdinfo[NDRC];
u_short	drcstd[] = { 0 };
struct	uba_driver drcdriver =
    { drcprobe, 0, drcattach, 0, drcstd, "drc", drcdinfo };

#define	DRCUNIT(dev)	(minor(dev))

drcprobe(reg)
	caddr_t reg;
{
	register int br, cvec;
	register struct drcdevice *drcaddr = (struct drcdevice *)reg;

/*
	drcaddr->drccsr = DRC_IEA|DRC_IEB;
	DELAY(10000);
	drcaddr->drccsr = 0;
*/
	br = 0x16;
	cvec = 0140;
}

/*ARGSUSED*/
drcattach(ui)
	register struct uba_device *ui;
{

}

drcopen(dev)
	dev_t dev;
{
	register struct drc_softc *sc;
	register struct uba_device *ui;
	register struct drcdevice *drcaddr;
	register i, junk;

	if (DRCUNIT(dev) >= NDRC || (ui = drcdinfo[DRCUNIT(dev)]) == 0 ||
	    ui->ui_alive == 0 || (sc = &drc_softc[DRCUNIT(dev)])->sc_openf) {
		u.u_error = ENXIO;
		return;
	}
	drcaddr = (struct drcdevice *)ui->ui_addr;
	sc->sc_procp = u.u_procp;	/* used for signals later */
	sc->sc_ttyp = u.u_ttyp;		/* controlling terminal for error mes */
	sc->sc_nice = u.u_procp->p_nice;
	u.u_procp->p_nice = 1;
	sc->sc_openf = 1;
	sc->sc_setup = 0;
	sc->sc_vslocked = 0;
	drcaddr->drccsr = 0;
	/*
	 * Suck the FIFO dry first... Give up after 1024 since
	 * it will be a hardware problem.
	 */
	i = 0;
	while ((drcaddr->drccsr & FIFO_EMPTY) == 0) {
		junk = drcaddr->drcbuf;
		if (i++ > 1024) {
			uprintf("Robot FIFO hung full -- hardware problems\n");
			drcclose(dev);
			u.u_error = EIO;
			break;
		}
	}
}

drcclose(dev)
	dev_t dev;
{
	register struct drc_softc *sc = &drc_softc[DRCUNIT(dev)];
	register struct drcdevice *drcaddr =
	    (struct drcdevice *)drcdinfo[DRCUNIT(dev)]->ui_addr;

	
	drcaddr->drccsr = 0;
	sc->sc_setup = 0;
	if (sc->sc_vslocked)
		vsunlock(0, sc->sc_vslocked);
	u.u_procp->p_flag &= ~SLOCK;
	sc->sc_procp = (struct proc *) 0;
	sc->sc_ttyp = (struct tty *) 0;
	u.u_procp->p_nice = sc->sc_nice;
	drc_softc[DRCUNIT(dev)].sc_openf = 0;
}

drcread(dev)
{

	u.u_error = ENXIO;
}

/*
 * For now, the write routine just inits the sc struct from
 * a user supplied drcrobot...
 */
drcwrite(dev)
	dev_t dev;
{
	register struct drc_softc *sc = &drc_softc[DRCUNIT(dev)];
	register struct drcdevice *drcaddr =
	    (struct drcdevice *)drcdinfo[DRCUNIT(dev)]->ui_addr;
	struct drcROBOT drcROBOT;

	if (copyin(u.u_base, &drcROBOT, sizeof (struct drcROBOT))) {
		u.u_error = EFAULT;
		return;
	}
	sc->sc_routine = drcROBOT.R_routine;
	sc->sc_routine2 = drcROBOT.R_routine2;
	sc->sc_rbuf = drcROBOT.R_rbuf;
	sc->sc_wbuf = drcROBOT.R_wbuf;

	/*
	 * Lock down all the pages in text + data region
	 */


	sc->sc_vslocked = ctob(u.u_tsize + u.u_dsize);
if(drcdebug)
printf("locking down %d bytes\n", sc->sc_vslocked);
	vslock(0, sc->sc_vslocked);
	u.u_procp->p_flag |= SLOCK;	/* lock in core */
	sc->sc_P0BR = mfpr(P0BR);
	sc->sc_P0LR = mfpr(P0LR);
	sc->sc_P1BR = mfpr(P1BR);
	sc->sc_P1LR = mfpr(P1LR);
	sc->sc_setup = 1;
	drcaddr->drccsr = DRC_IEA;

	u.u_base += sizeof (struct drcROBOT);
	u.u_count -= sizeof (struct drcROBOT);

}

drcintr(dev)
	dev_t dev;
{
	int c;
	register struct drc_softc *sc = &drc_softc[DRCUNIT(dev)];
	register struct drcdevice *drcaddr =
	    (struct drcdevice *)drcdinfo[DRCUNIT(dev)]->ui_addr;
	register count;
	register short *fp = (short *)&drcaddr->drcbuf;
	register short *sp = (short *)sc->sc_rbuf;
	int oldP0BR, oldP0LR, oldP1BR, oldP1LR;


if(drcdebug)
printf("I%d\n",dev);
	
	spl7();
	/*
	 * Save current user-page table pointer
	 * and switch to user (P0,P1 region) page
	 * table mapping for the process running
	 * the robot, run his signal routine, and switch
	 * back P0BR and P0LR to this proc.
	 * This is playing with FIRE !!
	 * THIS IS VAX-11/780 MACHINE DEPENDENT !!!!
	 * ("Kentucky" process switch)
	 */
	oldP0BR = mfpr(P0BR);
	oldP0LR = mfpr(P0LR);
	oldP1BR = mfpr(P1BR);
	oldP1LR = mfpr(P1LR);
	mtpr(P0BR, sc->sc_P0BR);
	mtpr(P0LR, sc->sc_P0LR);
	mtpr(P1BR, sc->sc_P1BR);
	mtpr(P0BR, sc->sc_P0BR);
	mtpr(TBIA, 0);	/* Invalidate translation buffer */
	if (drcaddr->drccsr&FIFO_EMPTY) {	/* spur intr */
		uprintf("Spur Robot interrupt\n");
		sc->sc_setup = 0;	/* blow him away */
		goto out;
	}
	if (sc->sc_routine) {
		count = (int) (short) *fp;
		if (count < 0 || count > 63) {
			int i, junk;

			uprintf("Bad robot count: 0%o\n", count);
			while ((drcaddr->drccsr & FIFO_EMPTY) == 0) {
				junk = drcaddr->drcbuf;
				if (i++ > 1024) {
					uprintf("Robot FIFO hung full -- hardware problems\n");
					sc->sc_setup = 0;/* blow him away */
					goto out;
				}
			}
			sc->sc_setup = 0;	/* blow him away */
			goto out;
		}
		*sp++ = count;
		while (count--)
			*sp++  = *fp;	/* suck it all out */

		if ((drcaddr->drccsr & FIFO_EMPTY) == 0) {
			uprintf("drcintr: FIFO not empty\n");
			sc->sc_setup = 0;	/* blow him away */
		}
		(*sc->sc_routine)();
		sp = (short *) sc->sc_wbuf;
		*fp = count = *sp++;
		while (count--)
			*fp = *sp++;
		if (sc->sc_routine2)
			(*sc->sc_routine2)();
	} else
		uprintf("No user interrupt routine\n");
out:
	mtpr(P0BR, oldP0BR);
	mtpr(P0LR, oldP0LR);
	mtpr(P1BR, oldP1BR);
	mtpr(P1LR, oldP1LR);
	mtpr(TBIA, 0);
	if (!sc->sc_setup) {
		drcaddr->drccsr = 0;	/* Clear IENB */
		if (sc->sc_procp)
			psignal(sc->sc_procp, SIGHUP);	/* blow him away */
	}
}

drcreset()
{
}

drcioctl()
{
}

#ifdef notdef
drchung(dev, s, interrupt)
register dev_t dev;
register char *s;
int interrupt;
{
	register struct drc_softc *sc = &drc_softc[DRCUNIT(dev)];
	register struct drcdevice *drcaddr =
	    (struct drcdevice *)drcdinfo[DRCUNIT(dev)]->ui_addr;
	register pri;
	struct tty *savettyp;

	interrupt = 1; /* for now */
	if (interrupt) {
		pri = spl7();
		savettyp = u.u_ttyp;
		u.u_ttyp = sc->sc_ttyp;
	}
	uprintf("DR11C Hung, Csr: 0%o, Ob: 0%o, Ib: 0%o %s\n",
		drcaddr->drccsr, drcaddr->drcobuf, drcaddr->drcibuf, s);
	
	if (interrupt) {
		u.u_ttyp = savettyp;
		sc->sc_setup = 0;	/* abort this device */
		splx(pri);
	} else {
		u.u_error = EIO;
	}

	printf("DR11C Hung, Csr: 0%o, Ob: 0%o, Ib: 0%o %s\n",
		drcaddr->drccsr, drcaddr->drcobuf, drcaddr->drcibuf, s);

}
#endif

#endif
