2.11BSD/sys/pdpuba/lp.c
/*
* Copyright (c) 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)lp.c 1.3 (2.11BSD GTE) 1997/2/14
*/
#include "lp.h"
#if NLP > 0
/*
* LP-11 Line printer driver
*
* This driver has been modified to work on printers where
* leaving LP_IE set would cause continuous interrupts.
*/
#include "param.h"
#include "systm.h"
#include "user.h"
#include "ioctl.h"
#include "tty.h"
#include "uio.h"
#include "kernel.h"
#define LPPRI (PZERO + 8)
#define LP_IE 0100 /* interrupt enable */
#define LP_RDY 0200 /* ready */
#define LP_ERR 0100000 /* error */
#define LPLWAT 40
#define LPHWAT 400
#define LPBUFSIZE 512
#define CAP 1
#ifndef LP_MAXCOL
#define LP_MAXCOL 132
#endif
#define LPUNIT(dev) (minor(dev) >> 3)
struct lpdevice {
short lpcs;
short lpdb;
};
struct lp_softc {
struct clist sc_outq;
int sc_state;
int sc_physcol;
int sc_logcol;
int sc_physline;
char sc_flags;
int sc_lpchar;
} lp_softc[NLP];
struct lpdevice *lp_addr[NLP];
int lptout();
/* bits for state */
#define OPEN 1 /* device is open */
#define TOUT 2 /* timeout is active */
#define MOD 4 /* device state has been modified */
#define ASLP 8 /* awaiting draining of printer */
lpattach(addr, unit)
struct lpdevice *addr;
register u_int unit;
{
if (unit >= NLP)
return (0);
lp_addr[unit] = addr;
return (1);
}
/*ARGSUSED*/
lpopen(dev, flag)
dev_t dev;
int flag;
{
register struct lp_softc *sc;
register int unit, s;
if ((unit = LPUNIT(dev)) >= NLP ||
(sc = &lp_softc[unit])->sc_state&OPEN ||
lp_addr[unit] == 0)
return (ENXIO);
if (lp_addr[unit]->lpcs&LP_ERR)
return (EIO);
sc->sc_state |= OPEN;
sc->sc_flags = minor(dev) & 07;
s = spl4();
if ((sc->sc_state&TOUT) == 0) {
sc->sc_state |= TOUT;
timeout(lptout, (caddr_t)dev, 10*hz);
}
splx(s);
lpcanon(dev, '\f');
return (0);
}
/*ARGSUSED*/
lpclose(dev, flag)
dev_t dev;
int flag;
{
register struct lp_softc *sc = &lp_softc[LPUNIT(dev)];
lpcanon(dev, '\f');
sc->sc_state &= ~OPEN;
}
lpwrite(dev, uio, flag)
register dev_t dev;
register struct uio *uio;
int flag;
{
register int n;
register char *cp;
char inbuf[LPBUFSIZE];
int error;
while (n = MIN(LPBUFSIZE, uio->uio_resid)) {
cp = inbuf;
error = uiomove(cp, (int)n, uio);
if (error)
return (error);
do
lpcanon(dev, *cp++);
while (--n);
}
return (0);
}
lpcanon(dev, c)
dev_t dev;
register int c;
{
struct lp_softc *sc = &lp_softc[LPUNIT(dev)];
register int logcol, physcol, s;
if (sc->sc_flags&CAP) {
register c2;
if (c >= 'a' && c <= 'z')
c += 'A'-'a'; else
switch (c) {
case '{':
c2 = '(';
goto esc;
case '}':
c2 = ')';
goto esc;
case '`':
c2 = '\'';
goto esc;
case '|':
c2 = '!';
goto esc;
case '~':
c2 = '^';
esc:
lpcanon(dev, c2);
sc->sc_logcol--;
c = '-';
}
}
logcol = sc->sc_logcol;
physcol = sc->sc_physcol;
if (c == ' ')
logcol++;
else switch(c) {
case '\t':
logcol = (logcol + 8) & ~7;
break;
case '\f':
if (sc->sc_physline == 0 && physcol == 0)
break;
/* fall into ... */
case '\n':
lpoutput(dev, c);
if (c == '\f')
sc->sc_physline = 0;
else
sc->sc_physline++;
physcol = 0;
/* fall into ... */
case '\r':
s = spl4();
logcol = 0;
lpintr(LPUNIT(dev));
splx(s);
break;
case '\b':
if (logcol > 0)
logcol--;
break;
default:
if (logcol < physcol) {
lpoutput(dev, '\r');
physcol = 0;
}
if (logcol < LP_MAXCOL) {
while (logcol > physcol) {
lpoutput(dev, ' ');
physcol++;
}
lpoutput(dev, c);
physcol++;
}
logcol++;
}
if (logcol > 1000) /* ignore long lines */
logcol = 1000;
sc->sc_logcol = logcol;
sc->sc_physcol = physcol;
}
lpoutput(dev, c)
dev_t dev;
int c;
{
register struct lp_softc *sc = &lp_softc[LPUNIT(dev)];
int s;
if (sc->sc_outq.c_cc >= LPHWAT) {
s = spl4();
lpintr(LPUNIT(dev)); /* unchoke */
while (sc->sc_outq.c_cc >= LPHWAT) {
sc->sc_state |= ASLP; /* must be LP_ERR */
sleep((caddr_t)sc, LPPRI);
}
splx(s);
}
while (putc(c, &sc->sc_outq))
sleep((caddr_t)&lbolt, LPPRI);
}
lpintr(lp11)
int lp11;
{
register int n;
register struct lp_softc *sc = &lp_softc[lp11];
register struct lpdevice *lpaddr = lp_addr[lp11];
lpaddr->lpcs &= ~LP_IE;
n = sc->sc_outq.c_cc;
if (sc->sc_lpchar < 0)
sc->sc_lpchar = getc(&sc->sc_outq);
while ((lpaddr->lpcs & LP_RDY) && sc->sc_lpchar >= 0) {
lpaddr->lpdb = sc->sc_lpchar;
sc->sc_lpchar = getc(&sc->sc_outq);
}
sc->sc_state |= MOD;
if (sc->sc_outq.c_cc > 0 && (lpaddr->lpcs&LP_ERR) == 0)
lpaddr->lpcs |= LP_IE; /* ok and more to do later */
if (n>LPLWAT && sc->sc_outq.c_cc<=LPLWAT && sc->sc_state&ASLP) {
sc->sc_state &= ~ASLP;
wakeup((caddr_t)sc); /* top half should go on */
}
}
lptout(dev)
dev_t dev;
{
register struct lp_softc *sc;
register struct lpdevice *lpaddr;
sc = &lp_softc[LPUNIT(dev)];
lpaddr = lp_addr[LPUNIT(dev)];
if ((sc->sc_state&MOD) != 0) {
sc->sc_state &= ~MOD; /* something happened */
/* so don't sweat */
timeout(lptout, (caddr_t)dev, 2*hz);
return;
}
if ((sc->sc_state&OPEN) == 0 && sc->sc_outq.c_cc == 0) {
sc->sc_state &= ~TOUT; /* no longer open */
lpaddr->lpcs = 0;
return;
}
if (sc->sc_outq.c_cc && (lpaddr->lpcs&LP_RDY) &&
(lpaddr->lpcs&LP_ERR)==0)
lpintr(LPUNIT(dev)); /* ready to go */
timeout(lptout, (caddr_t)dev, 10*hz);
}
#endif