/*
 *  Line printer driver
 */

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

#ifdef  SCCS_ID
static char SCCS_ID [] = "@(#)lpb.c    	3.2	 15:00:11 - 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	lpbaddr;	/* 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 */
} lpb;

/* 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
 */

lpbopen(dev, flag)
{
	if(lpb.state&ISOPEN || (ss(lpbaddr)&(NO_PAPER|INTLOCK|DU))) {
		u.u_error = EIO;
		return;
	}

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

	lpb.state = ISOPEN;
	lpb.flag = INITFLAGS;

	if (lpb.mxc == 0) {
		lpb.mxc = LPWIDTH;
		lpb.mxl = LPDEPTH;
	}
	oc(lpbaddr, ENABLE);
	lpbcanon(FORM);
}

/*
 *   close - line printer
 */

lpbclose(dev, flag)
{

	lpbcanon(FORM);
	trace(01<<8,"lpbclose",0);
	lpb.state = 0;
}

/*
 *     user write to line printer
 */

lpbwrite()
{
	register c;

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

/*
 * lpbcanon - character transformations
 */

lpbcanon(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 (lpb.flag & RAW) {
		lpboutput(c);
		return;
	}

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

	switch(c&0x7f) {

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

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

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

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

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

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

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

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


/*
 *   start transmission to printer
 */

lpbstart()
{
	register c;
	register s;

	trace(02<<8,"lpbstart",lpbaddr);
	trace(01<<8,"lpbstat",ss(lpbaddr));

	while( (s = ss(lpbaddr)) == 0
		&&    (c = getc(&lpb.outq)) >= 0  ) {
			trace(010<<8,"lpbchar",c);
			wd(lpbaddr, c);
		}
	trace(01<<8,"lpbstat",s);
}


/*
 *   line printer interrupt
 */

lpbint(dev, stat)
{
	trace(02<<8,"lpbint",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))) {
		lpbstart();
		if(lpb.outq.c_cc <= LPLWAT && lpb.state & ASLEEP) {
			trace(04<<8,"lpbwakeup",lpb.outq.c_cc);
			lpb.state &= ~ASLEEP;
			wakeup(&lpb);
		}
	}
}

/*
 *  write a character to line printer
 */

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

	putc(c, &lpb.outq);
	lpbstart();
	spl0();
}

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

lpbioctl(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;
			}
			lpb.mxc = ioc6.ioc6_erase;
			lpb.mxl = ioc6.ioc6_kill;
			lpb.flag = ioc6.ioc6_flags;
			break;
		}
#endif
		if (copyin(addr, (caddr_t)&iocb, sizeof(iocb))) {
			u.u_error = EFAULT;
			break;
		}
		lpb.mxc = iocb.ioc_erase;
		lpb.mxl = iocb.ioc_kill;
		lpb.flag = iocb.ioc_flags;
		break;

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