/*
 *  Line printer driver
 */

#include "../h/local.h"

#ifdef  SCCS_ID
static char SCCS_ID [] = "@(#)lpa.c    	3.2	 14:59:53 - 83/04/11 ";
#endif  SCCS_ID

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

/* Configuration */

#define LPPRI	75	/* line printer wakeup priority */
#define LPLWAT 300        /* line printer low water mark */
#define LPHWAT 1000       /* line printer high water mark */

#define LPWIDTH	131	/* page width (not 132, as lp auto prints on 132nd char */
#define LPDEPTH 0       /* lines per page */

extern char	lpaaddr;	/* printer address */

struct {    /* info about the printer */
	struct clist outq;	/* output char queue */
	char flag;	/* current mode */
	char state;	/* internal status */
	short mcc;	/* actual column posn */
	short ccc;	/* logical column posn */
	short mlc;	/* curr line # */
	short mxc;	/* max line width */
	short mxl;	/* max page depth */
} lpa;

/* Printer status & commands */

#define ENABLE	0x40
#define DISARM	0xc0

#define NO_PAPER	0x40
#define INTLOCK		0x10
#define DU		0x01


#define	P_WASTE	ECHO	/* if on, printer wastes paper */
#define IND	CRMOD	/* if on, lines are indented 8 spaces */

#define INITFLAGS       (P_WASTE)       /* Default flags */

#define FORM 0x0c
#define VTAB 0x0b


/*
 *    open - line printer
 */

lpaopen(dev, flag)
{
	if(lpa.state&ISOPEN || (ss(lpaaddr)&(NO_PAPER|INTLOCK|DU))) {
		u.u_error = EIO;
		return;
	}

	trace(01<<8,"lpaopen",0);

	lpa.state = ISOPEN;
	lpa.flag = INITFLAGS;

	if (lpa.mxc == 0) {
		lpa.mxc = LPWIDTH;
		lpa.mxl = LPDEPTH;
	}
	oc(lpaaddr, ENABLE);
	lpacanon(FORM);
}

/*
 *   close - line printer
 */

lpaclose(dev, flag)
{

	lpacanon(FORM);
	trace(01<<8,"lpaclose",0);
	lpa.state = 0;
}

/*
 *     user write to line printer
 */

lpawrite()
{
	register c;

	while ((c=cpass()) >= 0)
		lpacanon(c);
}

/*
 * lpacanon - character transformations
 */

lpacanon(ac)
{
	register char c;
	register char *p;

	c = ac;

	if (c == '_')
		c |= 0x80;   /*
			      *   Interdata line printer i/face
			      *   turns x'5f' into x'3c' .....
			      *
			      *   But it isn't smart enough to
			      *   recognize x'df', fortunately.
			      */
	if (lpa.flag & RAW) {
		lpaoutput(c);
		return;
	}

	if (lpa.flag & LCASE) {		/* Uppercase-only printer */
		if (c>='a' && c<='z')
			c += 'A'-'a';
		else {
			p = "({)}!|^~'`";
			while (*p++)
				if (c == *p++) {
					lpacanon(p[-2]);
					lpa.ccc--;
					c = '-';
					break;
				}
		}
	}

	switch(c&0x7f) {

	case '\t':
		lpa.ccc = (lpa.ccc + 8) & ~7;
		return;

	case FORM:
	case '\n':
		if(lpa.mcc != 0 || lpa.mlc != 0 || lpa.flag & P_WASTE) {
			if(lpa.mcc) {
				lpaoutput('\r');  /* cause line to print */
			}
			lpa.mlc++;
			if(lpa.mlc > lpa.mxl && lpa.mxl)
				c = FORM;
			lpaoutput(c);
			if (c == FORM)
				lpa.mlc = 0;
			lpa.mcc = 0;
		}   /* now fall through to '\r' code */

	case '\r':
		lpa.ccc = 0;
		if (lpa.flag & IND)
			lpa.ccc = 8;
		return;

	case '\b':
		if (lpa.ccc > 0)
			lpa.ccc--;
		return;

	case 0x07:
		lpaoutput(c); /* ring bell, no char advance */
		return;

	case ' ':
		lpa.ccc++;
		return;

	case VTAB:
		if(lpa.mcc)
			lpacanon('\n');
		while(lpa.mlc & 7)
			lpacanon('\n');
		return;

	default:
		if ((c & 0x7f) < 0x20)	/* non printing - ignore */
			return;
		if(lpa.ccc < lpa.mcc) {
			lpaoutput('\r');    /* cause overprint */
			lpa.mcc = 0;
		}
		if (lpa.ccc < lpa.mxc) {
			while(lpa.ccc > lpa.mcc) {
				lpaoutput(' ');
				lpa.mcc++;
			}
			lpaoutput(c);
			lpa.mcc++;
		}
		lpa.ccc++;
	}
}


/*
 *   start transmission to printer
 */

lpastart()
{
	register c;
	register s;

	trace(02<<8,"lpastart",lpaaddr);
	trace(01<<8,"lpastat",ss(lpaaddr));

	while( (s = ss(lpaaddr)) == 0
		&&    (c = getc(&lpa.outq)) >= 0  ) {
			trace(010<<8,"lpachar",c);
			wd(lpaaddr, c);
		}
	trace(01<<8,"lpastat",s);
}


/*
 *   line printer interrupt
 */

lpaint(dev, stat)
{
	trace(02<<8,"lpaint",stat);

/***	if(stat & DU)
		printf("\nline printer offline\n");
	else if (stat & NO_PAPER)
		printf("\nline printer paper out\n");
	else {			***/   if(!(stat & (DU|NO_PAPER))) {
		lpastart();
		if(lpa.outq.c_cc <= LPLWAT && lpa.state & ASLEEP) {
			trace(04<<8,"lpawakeup",lpa.outq.c_cc);
			lpa.state &= ~ASLEEP;
			wakeup(&lpa);
		}
	}
}

/*
 *  write a character to line printer
 */

lpaoutput(c)
{
	trace(02<<8,"lpaoutput",c);
	spl4();
	while(lpa.outq.c_cc >= LPHWAT) {
		lpa.state |= ASLEEP;
		trace(04<<8,"lpasleep",lpa.outq.c_cc);
		sleep(&lpa, LPPRI);
	}

	putc(c, &lpa.outq);
	lpastart();
	spl0();
}

/*
 * ioctl:
 *	allow several line printer attributes to be dynamically altered
 */

lpaioctl(dev, cmd, addr, flag)
caddr_t addr;
{
	struct ttiocb iocb;
#ifdef SVC6
	struct v6iocb ioc6;
#endif

	switch (cmd) {

	case TIOCSETP:
	case TIOCSETN:
#ifdef SVC6
		if (u.u_svc6) {
			if (copyin(addr, (caddr_t)&ioc6, sizeof(ioc6))) {
				u.u_error = EFAULT;
				break;
			}
			lpa.mxc = ioc6.ioc6_erase;
			lpa.mxl = ioc6.ioc6_kill;
			lpa.flag = ioc6.ioc6_flags;
			break;
		}
#endif
		if (copyin(addr, (caddr_t)&iocb, sizeof(iocb))) {
			u.u_error = EFAULT;
			break;
		}
		lpa.mxc = iocb.ioc_erase;
		lpa.mxl = iocb.ioc_kill;
		lpa.flag = iocb.ioc_flags;
		break;

	case TIOCGETP:
#ifdef SVC6
		if (u.u_svc6) {
			ioc6.ioc6_erase = lpa.mxc;
			ioc6.ioc6_kill = lpa.mxl;
			ioc6.ioc6_flags = lpa.flag;
			if (copyout((caddr_t)&ioc6, addr, sizeof(ioc6)))
				u.u_error = EFAULT;
			break;
		}
#endif
		iocb.ioc_erase = lpa.mxc;
		iocb.ioc_kill = lpa.mxl;
		iocb.ioc_flags = lpa.flag;
		if (copyout((caddr_t)&iocb, addr, sizeof(iocb)))
			u.u_error = EFAULT;
		break;
	}
}
