/*
 * Interdata versatec model 1200A driver
 *  Version 7 by Jim Rees Oct 1980
 *
 * Makes use of wb() (write block) if WB is defined --
 *  this routine must be added to ../conf/mch.s

wb      equ     *       write block
	l       r1,0(sp)
	wb      r1,4(sp)
	br      rf
 */

#include "../h/param.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/tty.h"

#define WB              /* wb() exists in ../conf/mch.s */
#define min(A,B)        ((A) < (B) ? (A) : (B))

 /* tuneable constants */

#define NVP       1     /* # of line printers */
#define VPPRI     28    /* Versatec priority */
#define LPBASE  0x62    /* starting address */
			/* These next two depend on the Versatec model */
#define CBUFSIZ	 132	/* character buffer size */
#define PLBUFSIZ 264	/* plot buffer size */

 /* commands to controller */

#define CLEAR	04
#define REOT	05
#define RLF	06
#define RFF	07
#define RESET	014
#define PLOT	020
#define SPP	040
#define DISARM	0300
#define ENABLE	0100
#define PRINT	0
#define MODE	(PLOT | SPP | ENABLE | DISARM)

 /* status from controller */

#define NOPWR    01
#define NOPAP    04
#define V_BUSY	010

 /* flags */

#define ODD	  01	/* Odd count */
#define OPEN      02  /* if device open for plot or print */

struct lp {
	short   flags;
	short   mode;    /* command register copy */
	short   addr;    /* physical address of device */
	short   nplchar;        /* number of bytes plotted this line */
	struct  buf lpb; /* buffer header */
} vp[NVP];

char vpmodes[] = {
	0,
	RESET,
	CLEAR,
	REOT,
	RFF,
	RLF,
	PRINT,
	PLOT,
	SPP
};

vpopen(dev)
int dev;
{
	struct lp *alp;
	struct buf *abp;

	alp = &vp[minor(dev) % 64];
	if (alp->flags & OPEN) {  /*  if already open  */
		u.u_error = EACCES;
		return;
	}
	if (minor(dev) < 64)
		alp->mode = PRINT;
	else
		alp->mode = PLOT;
	if (minor(dev)%64 >= NVP)  {
		u.u_error = ENXIO;
		return;
	}
	alp->addr = LPBASE + 2 * (minor(dev)%64);
	abp = &alp->lpb;
	abp->b_flags = 0;
	abp->b_bcount = 0;
	if (ss(alp->addr) & (NOPAP | NOPWR | V_BUSY)) {
		u.u_error = EIO;
		return;
	}
	alp->nplchar = 0;
	alp->flags = OPEN;
	alp->mode |= ENABLE;
	oc(alp->addr,alp->mode | CLEAR);
	trace(01<<8, "vpopen", alp->mode);
}

 /*
  * close printer
  */

vpclose(dev)
{
	struct lp *alp;
	struct buf *abp;

	trace(01<<8, "vpclose", dev);
	alp = &vp[minor(dev) % 64];
	abp = &alp->lpb;
	vprdy(abp);
	abp->b_flags |= B_BUSY;
	oc(alp->addr, ENABLE | RFF);
	vprdy(abp);
	alp->mode = DISARM;
	oc(alp->addr,alp->mode);
	alp->flags = 0;
}

vpwrite(dev)
{
	register struct lp *alp;
	int vpstrategy();

	alp = &vp[minor(dev) % 64];
	alp->flags &= ~ODD;
	alp->flags |= u.u_count & ODD;	/* Make count even to satisfy physio */
	u.u_count &= ~ODD;
	trace(04<<8, "write", u.u_count);
	physio(vpstrategy, &alp->lpb, dev, B_WRITE);
}

vpstrategy(abp)
register struct buf *abp;
{
	struct lp *alp;

	trace(04<<8,"vpstrategy", abp->b_bcount);
	alp = &vp[minor(abp->b_dev) % 64];

	abp->b_bcount |= alp->flags & ODD;	/* In case count was odd */
	if (abp->b_bcount > 0)
		vpstart(alp);
}

vpstart(alp)
struct lp *alp;
{
	register char *i,*ilast;
	register count;
	struct buf *abp;

	abp = &alp->lpb;
	trace(04<<8,"vpstart", abp->b_bcount);
	if ((count = abp->b_bcount) == 0)
		return;

	i = abp->b_un.b_addr;
	abp->b_flags |= B_BUSY;

	if (alp->mode&PLOT) {
		ilast = i + min(PLBUFSIZ - alp->nplchar, abp->b_bcount);
		count -= ilast - i;
		alp->nplchar += ilast - i;
		alp->nplchar %= PLBUFSIZ;
#ifdef WB
		wb(alp->addr, i, ilast-1);
		i = ilast;
#else
		while (i < ilast)
			wd(alp->addr,*i++);
#endif
	}
	else {
		ilast = i + min(CBUFSIZ, count);
		while (i < ilast) {
			wd(alp->addr,*i++);
			count--;
			if (*(i-1) == '\n' || *(i-1) == '\f')
				break;
		}
		if (alp->mode & SPP) {
			alp->mode |= PLOT;
			oc(alp->addr,alp->mode|CLEAR);
		}
	}
	abp->b_un.b_addr += abp->b_bcount - count;
	abp->b_resid = abp->b_bcount = count;
}

vprdy(abp)
register struct buf *abp;
{
	iowait(abp);
	spl4();
	while (abp->b_flags & B_BUSY)
		sleep(abp, VPPRI);
	spl0();
}

 /*
  * interrupt handling routine
  */
vpint(dev,stat)
{
	register struct lp *alp;
	register struct buf *abp;

	trace(8<<8, "vpint", stat);
	alp = &vp[minor(dev) % 64];
	abp = &alp->lpb;
	if (stat&NOPAP) {
		printf("Versatec: out of paper\n");
		return;
	}
	if (stat&NOPWR) {
		printf("Versatec: not ready\n");
		return;
	}
	if (stat&V_BUSY)
		return;
	abp->b_flags &= ~B_BUSY;

	if (abp->b_bcount > 0 && alp->flags)
		vpstart(alp);
	else
		iodone(abp);
}

vpioctl(dev, cmd, addr, flag)
int cmd, flag;
int *addr;
{
	int v[1];
	register i;
	struct lp *alp;
	struct buf *abp;

	alp = &vp[minor(dev) % 64];
	cmd &= 07;              /* Sort of a kluge */

	if (cmd == 0) {                 /* gtty */
		v[0] = 0;
		for (i=0; i<9; i++)
			if (alp->mode & (vpmodes[i]))
				v[0] |= 1<<i;
		if (copyout(v, addr, 4)) {
			u.u_error = EFAULT;
			return(1);
		}
	}

	else if (cmd == 1) {                    /* stty */
		abp = &alp->lpb;
		vprdy(abp);
		alp->nplchar = 0;
		alp->mode &= (ENABLE | DISARM);
		if (copyin(addr, v, 4)) {
			u.u_error = EFAULT;
			return(1);
		}
		for (i=0; i<9; i++)
			if (v[0] & 1<<i)
				alp->mode |= vpmodes[i];
		abp->b_flags |= B_BUSY;
		oc(alp->addr, alp->mode | CLEAR);
		trace(1<<8, "vpioctl", alp->mode);
		alp->mode &= MODE;
	}
	return(0);
}
