AUSAM/sys/dmr/lp.c-elec

#
/*
 *	Copyright 1976 Ian Johnstone.
 *
 *
 *	This line printer driver will handle printers
 *		in the best most efficient way. Maybe.
 *	No upper to lower case conversion.
 *	Recognize zero, blank suppression.
 *	Support adjustable size pages.
 *	Support 013 as skip to perforations.
 *	Talk to DEC and other ( CDC ) printers.
 *	Support special CDC skips.
 *	Kronos eor/eof taken as form feeds. 022, 006
 *	Stty calls supported to change modes.
 *
 */


#include "../defines.h"
#include "../param.h"
#include "../conf.h"
#include "../user.h"
#include "../proc.h"
#include "../buf.h"



/*
 * status register bits
 */

#define	IENABLE	0100
#define	READY	0200

#define	NLP	1	/* no. of line printers */
#define	LPPRI	3

/*
 * some status & mode bits
 */

#define	WOPEN	0000001		/* device open for writing */
#define	CLOSIN	0000002		/* device closing */

#define	TRNS	0000004		/* if half ASCII (May be undefined) */
#define	CDC	0000010		/* if cdc special skips  */
#define	VTAB	0000020		/* support vertical tabs */
#define	FLUSH	0000040		/* when set flush all */
#define	FEED	0000100		/* if always take ff's, lf's */
#define	NOEJECT	0000200		/* if disable auto skip */
#define	TMPNOEJ	0000400		/* if temporary NOEJECT */
#define	SIXLPI	0001000		/* 11" paper at 6 lines/inch */


				/* this fixes a glitch in UNSW interface */
#define	POWROFF	0040000		/* printer has been powered off */
#define	ERROR	0100000		/* Error bit set in status register */

#ifdef	TRNS
#define	MODES	(TRNS|CDC|VTAB|FLUSH|FEED|NOEJECT|SIXLPI)
#else
#define	MODES	(CDC|VTAB|FLUSH|FEED|NOEJECT|SIXLPI)
#endif	TRNS

/*
 * define characteristics of devices
 */

#define	LPF0	(TRNS|CDC|VTAB)	/* cdc printer */
#define	MAXCOL	136			/* cdc printer */
#define	PL	60			/* page length */

#define	BMAX	04	/* maximum number of buffers used per device */

int		lpages;	/* pages printed since last boot (in toto) */


struct lpdev
{
	int lpsr;
	int lpbuf;
};
struct { int lpuf;};	/* dummy for lpint only */

struct lpst
{
	int		flag;		/* control flags */
	int		bfree;		/* number of buffers unused */
	struct	buf    *bfwd;		/* first buffer in chain */
	struct	buf    *blst;		/* last  buffer in chain */
	struct	lpdev  *lpaddr;		/* device register address */
	int		ejline;		/* lines per page (settable) */
	unsigned	maxcol;		/* max cols per line (settable) */
	unsigned	ocol;		/* output column */
	unsigned	icol;		/* input column */
	int		lino;		/* output line */
	int		lini;		/* input line */
	unsigned	vtcnt;		/* count for vertical tabs */
	int		bc;		/* byte count (-ve) */
	char	       *ba;		/* next char in buffer */
	char		fc;		/* duplicated char if non-zero */
}
		lp0			/* define lp0 */
{
	LPF0,				/* flags */
	BMAX,				/* bfree */
	0, 0,				/* bfwd, blst */
	0177514,			/* lpaddr */
	PL,				/* ejline */
	MAXCOL,				/* maxcol */
	0, 0,				/* ocol, icol */
	0, 0,				/* lino, lini */
	0,				/* vtcnt */
	0, 0,				/* bc, ba */
	0,				/* fc */
},
#ifdef	TWO_PRINTERS
		lp1			/* define lp1 */
{
	LPF1,				/* flags */
	BMAX,				/* bfree */
	0, 0,				/* bfwd, blst */
	0160024,			/* lpaddr */
	PL,				/* ejline */
	MAXCOL,				/* maxcol */
	0, 0,				/* ocol, icol */
	0, 0,				/* lino, lini */
	0,				/* vtcnt */
	0, 0,				/* bc, ba */
	0,				/* fc */
},
#endif	TWO_PRINTERS
		*lp11[NLP]		/* define lp11 */
{
	&lp0,				/* printer 0 */
#ifdef	TWO_PRINTERS
	&lp1,				/* printer 1 */
#endif	TWO_PRINTERS
};

#define	lperror	(!fkword(plp->lpaddr) || plp->lpaddr->lpsr < 0)

lpopen(dev, flag)
{
	register struct lpst *plp;

	plp = lp11[dev.d_minor];
	if(dev.d_minor >= NLP)
	{
		u.u_error = ENXIO;
		return;
	}
	else if(flag)
	{
		register mode = FLUSH|ERROR;

		if((plp->flag&WOPEN) || lperror)
		{
			u.u_error = EOPENFAIL;
			return;
		}
		if(plp->flag & TMPNOEJ)
			mode =| (NOEJECT | TMPNOEJ);
		plp->flag =& ~mode;
		plp->flag =| WOPEN;
		plp->lpaddr->lpsr =| IENABLE;
		plp->icol = plp->ocol = plp->lino = plp->lini = 0;
	}
}

lpclose(dev, flag)
{
	register struct lpst *plp = lp11[dev.d_minor];

	if( plp->flag&WOPEN )
	{
		/* if final close after write */
		spl4();
		plp->flag =| CLOSIN;
		if( plp->bfwd == 0 )
			lpint(dev);
		spl0();
	}
}

lpwrite(dev)
{
	register struct buf  *bp;
	register struct lpst *plp;
	register int n;

	plp = lp11[dev.d_minor];
	spl4();
	while(u.u_count && !u.u_error)
	{
		while(!plp->bfree)
		{
			sleep(plp,LPPRI);
		}
		if( plp->flag & FLUSH )
		{
			u.u_error = EIO;
			break;
		}
		plp->bfree--;
		bp = getblk(NODEV);
		bp->av_forw = 0;
		spl4();
		if( (n = u.u_count) > 512 )
			n = 512;
		bp->b_wcount = -n;
		iomove(bp, 0, n, B_WRITE);
		if(!plp->bfwd)
		{
			plp->bfwd = plp->blst = bp;
#ifdef	MAPPED_BUFFERS
			plp->ba = b.buff;
#else	MAPPED_BUFFERS
			plp->ba = bp->b_addr;
#endif	MAPPED_BUFFERS
			plp->bc = bp->b_wcount;
			lpint(dev);
		}
		else
		{
			plp->blst->av_forw = bp;
			plp->blst = bp;
		}
	}
	spl0();
}

lpint(dev)
{
	register struct lpst *plp;
	register int *lpa;
#ifdef	MAPPED_BUFFERS
	int ka5sav = ka5;
#endif	MAPPED_BUFFERS

	plp = lp11[dev.d_minor];
	while(!fkword(plp->lpaddr));
	if(plp->lpaddr->lpsr < 0)
	{
		if((plp->lpaddr->lpsr & POWROFF) && !(plp->flag & POWROFF))
		{
			plp->flag =| POWROFF;
			printf("LP%d POWERED OFF\n", dev);
		}
		plp->flag =| ERROR;
		plp->lpaddr->lpsr =& ~IENABLE;
		timeout(lpint, dev, 1*HZ);
		return;
	}
	plp->flag =& ~(ERROR|POWROFF);
	plp->lpaddr->lpsr =| IENABLE;
	if( plp->bfwd == 0 )
		goto fin;
	if( plp->flag & FLUSH )
	{
		register struct buf *bp;

		bp = plp->bfwd;
		do
		{
			lpa = bp->av_forw;
			brelse(bp);
			plp->bfree++;
		} while( bp = lpa );
		plp->bfwd = 0;
		wakeup(plp);
		goto fin;
	}
	lpa = &(plp->lpaddr->lpbuf);
	if( plp->vtcnt )
	{
		lpa->lpuf = '\n';
		plp->vtcnt--;
		return;
	}
#ifdef	MAPPED_BUFFERS
	bswtch(plp->bfwd);
#endif	MAPPED_BUFFERS
loop:
	while( (plp->lpaddr->lpsr & READY) && (plp->bc++ < 0) )
	{
		register int c1;

		c1 = (*plp->ba++) & 0377;
		if( plp->fc )
		{
			plp->icol =+ (--c1);
			if(plp->fc == '0')
			{
				if(plp->icol > plp->maxcol)
					c1 =- plp->icol - plp->maxcol;
				while(c1-- > 0)
					lpa->lpuf = '0';
			}
			plp->fc = 0;
			continue;
		}
		switch( c1 )
		{
	    case 0:
			break;
	    case '\t':
			plp->icol = (plp->icol+8) & ~7;
			break;
	    case 0377:
			plp->fc = c1 = ' ';
			goto dflt;
	    case 0376:
			plp->fc = c1 = '0';
			goto dflt;
	    case 0375:
			if( !(plp->flag & NOEJECT))
				plp->flag =| (NOEJECT | TMPNOEJ);
			break;
	    case 013:
			if( plp->flag & VTAB )
			{
				if( plp->flag & CDC )
				{
					lpa->lpuf = 0100103;
				}
				else
				{
					plp->vtcnt = plp->ejline - plp->lino + 3;
					if(plp->flag & SIXLPI)
						plp->vtcnt--;
					lpa->lpuf = '\n';
				}
				plp->icol = plp->ocol = 0;
				if(plp->flag & SIXLPI)
					c1 = -3;
				else
					c1 = -4;
				plp->lini = plp->lino = c1;
			}
			break;
	    case 006:	/* kronos eof */
	    case 022:	/* kronos eor */
			c1 = 014;
	    case 014:
	    case '\n':
			if( plp->flag & FEED || plp->lini || plp->ocol )
			{
				plp->ocol = 0;
				plp->lini++;
				if( plp->lini >= plp->ejline &&
					!(plp->flag & NOEJECT) )
				{
					c1 = 014;
				}
				if(c1 == 014)
				{ 
					lpa->lpuf = c1;
				    	plp->lini = plp->lino = 0;
					lpages++; 
				}
			}
	    case '\r':
			plp->icol = 0;
			break;
	    case 010:
			if(plp->icol > 0)
				plp->icol--;
			break;
	    case ' ':
			plp->icol++;
			break;
	    default:
		dflt:
			if(plp->lini > plp->lino)
			{
				if(plp->flag & CDC)
				{
					c1 = plp->lini - plp->lino;
					if(c1 > 3)
						c1 = 3;
					lpa->lpuf = 0100120 + c1;
					plp->lino =+ c1;
				}
				else
				{
					plp->vtcnt = plp->lini - plp->lino - 1;
					plp->lino = plp->lini;
					lpa->lpuf = '\n';
				}
				plp->fc = 0;
				--plp->ba;
				--plp->bc;
				break;
			}
			if(plp->icol < plp->ocol)
			{
				lpa->lpuf = '\r';
				plp->ocol = plp->fc = 0;
				--plp->ba;
				--plp->bc;
				break;
			}
			if(plp->icol < plp->maxcol)
			{
				while(plp->icol > plp->ocol)
				{
					lpa->lpuf = ' ';
					plp->ocol++;
				}
#ifdef	TRNS
				if( plp->flag & TRNS )
				{
					switch( c1 )
					{
				    case '{':	c1 = '(';
						goto esc;
				    case '}':	c1 = ')';
						goto esc;
				    case '`':	c1 = '\'';
						goto esc;
				    case '|':	c1 = '!';
						goto esc;
				    case '~':	c1 = '^';
			
					esc:
						lpa->lpuf = c1;
						*(--plp->ba) = '-';
						--plp->bc;
						plp->ocol = 0;
						lpa->lpuf = '\r';
						continue;
					}
				}
#endif	TRNS
				lpa->lpuf = c1;
				plp->ocol++;
			}
			if( ++plp->icol == 0) plp->icol--;
		}
	}
	if(plp->bc >= 0)
	{
		register struct buf *bp;

		bp = plp->bfwd;
		plp->bfwd = bp->av_forw;
		brelse(bp);
		if( !(plp->bfree++))
			wakeup(plp);
		if(bp = plp->bfwd)
		{
#ifndef	MAPPED_BUFFERS
			plp->ba = bp->b_addr;
#else	MAPPED_BUFFERS
			plp->ba = b.buff;
			bswtch(bp);
#endif	MAPPED_BUFFERS
			plp->bc = bp->b_wcount;
			goto loop;
		}
	fin:
		if( (plp->lpaddr->lpsr & READY) && (plp->flag & CLOSIN) )
		{
			plp->flag =& ~(WOPEN | CLOSIN);
			plp->lpaddr->lpsr = 0;
			if( plp->lino  || plp->ocol )
				plp->lpaddr->lpbuf = 014;
		}
	}
#ifdef	MAPPED_BUFFERS
	ka5 = ka5sav;
#endif	MAPPED_BUFFERS
}

lpsgtty( dev, v )
register int *v;
{
	register struct lpst *plp;

	plp = lp11[dev.d_minor];

	switch( v )
	{
		/*
		 *	set
		 */
	case 0:
		v = u.u_arg;
		spl4();			/* look out for interrupt */
		plp->flag = (*v++ & MODES) | (plp->flag & ~MODES); /* protect status */
		spl0();
		plp->ejline = *v++;
		plp->maxcol = *v++;
		return(0);

		/*
		 *	get
		 */
	default:
		*v++ = plp->flag;
		*v++ = plp->ejline;
		*v++ = plp->maxcol;
		return(1);
	}
}

#ifdef	POWER_FAIL
lppowf()
{
	register struct lpst *plp;
	register i;

	for( i = 0; i < NLP; i++)
	{
		plp = lp11[i];
		if( plp->flag & WOPEN)
			plp->lpaddr->lpsr =| IENABLE;
	}
}
#endif	POWER_FAIL