531 lines
11 KiB
C
531 lines
11 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)pp.c 1.1 94/10/31 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Parallel Port (printer) driver.
|
|
*/
|
|
|
|
#include "pp.h" /* file generated by CONFIG;
|
|
contains the definition of NPP */
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sundev/ppreg.h> /* register definitions */
|
|
#if NPP > 0
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/file.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <machine/psl.h>
|
|
|
|
|
|
/*
|
|
* Buffers for use by physio().
|
|
*/
|
|
struct buf ppbuf[NPP];
|
|
#define PPBUFSIZ 64 /* size of buffer written to printer */
|
|
|
|
/*
|
|
* software state structure, one for each printer
|
|
*/
|
|
struct ppstate {
|
|
int pp_flags; /* printer state: */
|
|
#define PP_OPEN 0x01 /* currently open */
|
|
#define PP_WANT 0x02 /* someone waiting for printer */
|
|
#define PP_TIMER 0x08 /* watchdog timer is running */
|
|
#define PP_BUSY 0x10 /* i/o in progress */
|
|
char pp_timer; /* for detecting timeout situations*/
|
|
u_char pp_lostintr; /* for tracking lost interrupts*/
|
|
u_char pp_notready; /* printer not ready (no paper, etc.) */
|
|
int pp_unit; /* unit number*/
|
|
struct mb_device *pp_md; /* pointer to mb info */
|
|
struct buf *pp_bp; /* pointer to current 'buf' */
|
|
char pp_buf[PPBUFSIZ]; /* buffer */
|
|
char *pp_cp; /* current byte in current buffer */
|
|
int pp_count; /* number of bytes left to print */
|
|
#if defined(SUN386)
|
|
u_short pp_regbase; /* device register base in i/o space */
|
|
#else /* !SUN386 */
|
|
struct pp_reg *pp_regbase; /* device register base in mb space */
|
|
#endif
|
|
} ppstate[NPP];
|
|
|
|
#if defined(SUN386)
|
|
#define PPREG_DATA (pp->pp_regbase)
|
|
#define PPREG_CTRL (pp->pp_regbase + 2)
|
|
#define PPREG_STAT (pp->pp_regbase + 1)
|
|
#else /* !SUN386 */
|
|
#define PPREG_DATA (pp->pp_regbase->pp_data)
|
|
#define PPREG_CTRL (pp->pp_regbase->pp_cntrl)
|
|
#define PPREG_STAT (pp->pp_regbase->pp_stat)
|
|
#endif
|
|
|
|
|
|
#define PPUNIT(dev) (minor(dev) & 0xf)
|
|
#define PPDIAG(dev) (minor(dev) & 0x10)
|
|
#define PPPRI (PZERO + 1) /* sleeps are interruptable */
|
|
|
|
extern int hz;
|
|
#define PPWATCHDOG 3 /* watchdog interval: see 'pptimeout()' */
|
|
#define PPTICKS (30/PPWATCHDOG + 1)
|
|
#define PPMSGTICKS (180/PPWATCHDOG)
|
|
|
|
#define DEBUG
|
|
#ifdef DEBUG
|
|
/*
|
|
* Debugging stuff.
|
|
*/
|
|
#define DBINIT 0x0001
|
|
#define DBIO 0x0002
|
|
#define DBOPEN 0x0004
|
|
#define DBCLOSE 0x0008
|
|
#define DBSTRAT 0x0010
|
|
#define DBSTART 0x0020
|
|
#define DBTMOUT 0x0040
|
|
#define DBINTR 0x0080
|
|
|
|
/*
|
|
int ppdebug = 0xffff;
|
|
*/
|
|
int ppdebug = 0;
|
|
#define ppprint(flg,x) if ((flg) & ppdebug) printf x
|
|
|
|
#else
|
|
#define ppprint(flg,x)
|
|
#endif DEBUG
|
|
|
|
int ppprobe(), ppattach(), ppintr(), pptimeout(), pppoll();
|
|
|
|
struct mb_driver ppdriver = {
|
|
ppprobe, 0, ppattach, 0, 0, pppoll, 0, "pp", 0, 0, 0, 0,
|
|
};
|
|
|
|
|
|
/*ARGSUSED*/
|
|
ppprobe(reg, unit)
|
|
caddr_t reg;
|
|
int unit;
|
|
{
|
|
ppprint(DBINIT, ("ppprobe\n"));
|
|
|
|
if (unit >= NPP) {
|
|
printf("pp: too many units");
|
|
return(0);
|
|
}
|
|
if (peekc(reg) == -1) {
|
|
return(0);
|
|
}
|
|
|
|
#if defined(SUN386)
|
|
ppstate[unit].pp_regbase = (u_short)reg;
|
|
#else /* !SUN386 */
|
|
ppstate[unit].pp_regbase = (struct pp_reg *)reg;
|
|
#endif
|
|
return(1);
|
|
}
|
|
|
|
ppattach(md)
|
|
register struct mb_device *md;
|
|
{
|
|
register struct ppstate *pp;
|
|
|
|
ppprint(DBINIT, ("ppattach\n"));
|
|
|
|
pp = &ppstate[md->md_unit];
|
|
pp->pp_md = md;
|
|
|
|
/* Initialize printer.
|
|
* Holding PC_INIT low for 50 msecs does the trick.
|
|
*/
|
|
#if defined(SUN386)
|
|
outb(PPREG_CTRL, PC_RESET);
|
|
DELAY(5000);
|
|
outb(PPREG_CTRL, PC_OFF);
|
|
DELAY(10);
|
|
#else /* !SUN386 */
|
|
PPREG_CTRL = PC_RESET;
|
|
DELAY(5000);
|
|
PPREG_CTRL = PC_OFF;
|
|
DELAY(10);
|
|
#endif
|
|
}
|
|
|
|
ppopen(dev, flags)
|
|
dev_t dev;
|
|
int flags;
|
|
{
|
|
register struct ppstate *pp = &ppstate[PPUNIT(dev)];
|
|
int status;
|
|
|
|
ppprint(DBOPEN, ("ppopen: unit %d\n", PPUNIT(dev)));
|
|
|
|
if (PPUNIT(dev) >= NPP || pp->pp_md->md_alive == 0)
|
|
return(ENXIO);
|
|
if ((PPDIAG(dev) == 0) & (flags & FREAD))
|
|
return(ENODEV);
|
|
|
|
pp->pp_unit = PPUNIT(dev);
|
|
|
|
while (pp->pp_flags & PP_OPEN) { /* enforce exclusive access */
|
|
ppprint(DBOPEN, ("ppopen: in use - waiting...\n"));
|
|
if (flags & FNDELAY)
|
|
return(EBUSY);
|
|
pp->pp_flags |= PP_WANT;
|
|
if (sleep((caddr_t)&pp->pp_flags, PPPRI|PCATCH)) {
|
|
return(EINTR);
|
|
}
|
|
}
|
|
|
|
#if defined(SUN386)
|
|
status = inb(PPREG_STAT);
|
|
#else /* !SUN386 */
|
|
status = PPREG_STAT;
|
|
#endif
|
|
if ((PPDIAG(dev) == 0) &&
|
|
(PSNOPAPER(status) || ! PSSELECT(status) || PSERROR(status))) {
|
|
if (PSNOPAPER(status))
|
|
uprintf("pp%d: printer out of paper\n", pp->pp_unit);
|
|
else
|
|
uprintf("pp%d: printer not ready\n", pp->pp_unit);
|
|
(void)wakeup((caddr_t)&pp->pp_flags);
|
|
pp->pp_flags = 0;
|
|
return(EIO);
|
|
}
|
|
|
|
#if defined(SUN386)
|
|
outb(PPREG_CTRL, PC_NORM); /* enable interrupts */
|
|
#else /* !SUN386 */
|
|
PPREG_CTRL = PC_NORM; /* enable interrupts */
|
|
#endif
|
|
DELAY(50);
|
|
|
|
if ((pp->pp_flags & PP_TIMER) == 0) {
|
|
/*
|
|
* Kick off watchdog timer.
|
|
*/
|
|
timeout(pptimeout, (caddr_t)pp, PPWATCHDOG*hz);
|
|
pp->pp_timer = 0;
|
|
pp->pp_flags |= PP_TIMER;
|
|
}
|
|
|
|
pp->pp_flags |= PP_OPEN;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* ppclose:
|
|
* Close the printer device.
|
|
* Turn off interrupts.
|
|
* Wake up anyone waiting to open the printer.
|
|
*/
|
|
ppclose(dev)
|
|
dev_t dev;
|
|
{
|
|
register struct ppstate *pp = &ppstate[PPUNIT(dev)];
|
|
|
|
ppprint(DBCLOSE, ("ppclose: unit %d\n", PPUNIT(dev)));
|
|
|
|
#if defined(SUN386)
|
|
outb(PPREG_CTRL, PC_OFF); /* disable interrupts */
|
|
#else /* !SUN386 */
|
|
PPREG_CTRL = PC_OFF; /* disable interrupts */
|
|
#endif
|
|
DELAY(50);
|
|
|
|
if (pp->pp_flags & PP_WANT)
|
|
wakeup((caddr_t)&pp->pp_flags);
|
|
untimeout(pptimeout, (caddr_t)pp);
|
|
pp->pp_flags = 0;
|
|
}
|
|
|
|
|
|
ppwrite(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
int ppstrategy();
|
|
void ppminphys();
|
|
|
|
ppprint(DBIO, ("ppwrite\n"));
|
|
|
|
return(physio(ppstrategy, &ppbuf[PPUNIT(dev)], dev, B_WRITE,
|
|
ppminphys, uio));
|
|
}
|
|
|
|
/*
|
|
* ppstrategy:
|
|
*/
|
|
ppstrategy(bp)
|
|
register struct buf *bp;
|
|
{
|
|
register struct ppstate *pp = &ppstate[PPUNIT(bp->b_dev)];
|
|
|
|
ppprint(DBSTRAT|DBIO, ("ppstrategy\n"));
|
|
|
|
pp->pp_bp = bp;
|
|
pp->pp_count = bp->b_bcount;
|
|
pp->pp_cp = pp->pp_buf;
|
|
|
|
if (copyin(bp->b_un.b_addr, pp->pp_buf, (u_int) bp->b_bcount)) {
|
|
bp->b_flags |= B_ERROR;
|
|
bp->b_error = EFAULT;
|
|
ppiodone(pp);
|
|
return;
|
|
}
|
|
|
|
pp->pp_flags |= PP_BUSY;
|
|
pp->pp_timer = PPTICKS; /* set timer */
|
|
pp->pp_lostintr = 0; /* reset "lost interrupt" counter */
|
|
pp->pp_notready = 0; /* reset "notready" counter */
|
|
(void) ppintr();
|
|
ppiowait(pp, bp);
|
|
pp->pp_timer = 0; /* turn off timer */
|
|
|
|
ppprint(DBSTRAT, ("ppstrategy: ***done\n"));
|
|
}
|
|
|
|
void
|
|
ppminphys(bp)
|
|
register struct buf *bp;
|
|
{
|
|
if (bp->b_bcount > PPBUFSIZ)
|
|
bp->b_bcount = PPBUFSIZ;
|
|
}
|
|
|
|
/*
|
|
* ppintr:
|
|
* Handle 'ack' interrupts from printer.
|
|
*/
|
|
int ppdelay1 = 1;
|
|
int ppdelay2 = 1;
|
|
ppintr()
|
|
{
|
|
register struct ppstate *pp;
|
|
int status; /* printer status */
|
|
int s;
|
|
|
|
pp = &ppstate[0]; /* XXX - only works for unit #0 */
|
|
|
|
s = splx(pritospl(pp->pp_md->md_intpri));
|
|
|
|
#if defined(SUN386)
|
|
status = inb(PPREG_STAT);
|
|
#else /* !SUN386 */
|
|
status = PPREG_STAT;
|
|
#endif
|
|
ppprint(DBINTR, ("ppintr: status = 0x%x\n", status));
|
|
|
|
/* Were we expecting an interrupt?
|
|
*/
|
|
if ((status & (PS_READY | PS_NOTACK)) != (PS_READY | PS_NOTACK)) {
|
|
(void) splx(s);
|
|
return(0);
|
|
}
|
|
|
|
|
|
if (pp->pp_count > 0) {
|
|
|
|
/* AT Tech Ref says data must be in data reg at least
|
|
* 0.5 usec before and after we strobe, and strobe must
|
|
* last at least 0.5 usec.
|
|
*/
|
|
#if defined(SUN386)
|
|
outb(PPREG_DATA, *pp->pp_cp);
|
|
pp->pp_cp++;
|
|
pp->pp_count--;
|
|
DELAY(1);
|
|
outb(PPREG_CTRL, PC_NORM|PC_STROBE);
|
|
DELAY(1);
|
|
outb(PPREG_CTRL, PC_NORM);
|
|
#else /* !SUN386 */
|
|
PPREG_DATA = *pp->pp_cp;
|
|
pp->pp_cp++;
|
|
pp->pp_count--;
|
|
DELAY(ppdelay1);
|
|
PPREG_CTRL = PC_NORM|PC_STROBE;
|
|
DELAY(ppdelay2);
|
|
PPREG_CTRL = PC_NORM;
|
|
#endif
|
|
}
|
|
|
|
else
|
|
ppiodone(pp);
|
|
|
|
(void) splx(s);
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
* pptimeout:
|
|
* Check occasionally for lost interrupts or
|
|
* printer errors (no paper, printer off line, etc.).
|
|
*/
|
|
pptimeout(arg)
|
|
caddr_t arg;
|
|
{
|
|
register struct ppstate *pp = (struct ppstate *)arg;
|
|
int status; /* printer status */
|
|
int error = 0;
|
|
int s;
|
|
|
|
ppprint(DBTMOUT, ("pptimeout\n"));
|
|
|
|
s = splx(pritospl(pp->pp_md->md_intpri));
|
|
|
|
|
|
/* If we're not currently doing anything, we can go away.
|
|
*/
|
|
if ((pp->pp_flags & PP_OPEN) == 0) { /* not open */
|
|
(void) splx(s);
|
|
return;
|
|
} else if (pp->pp_timer <= 0) { /* not currently active */
|
|
timeout(pptimeout, (caddr_t)pp, PPWATCHDOG*hz);
|
|
(void) splx(s);
|
|
return;
|
|
}
|
|
|
|
#if defined(SUN386)
|
|
status = inb(PPREG_STAT);
|
|
#else /* !SUN386 */
|
|
status = PPREG_STAT;
|
|
#endif
|
|
|
|
/* Check for printer errors.
|
|
*/
|
|
if (PSNOPAPER(status)) {
|
|
if ((pp->pp_notready++ % PPMSGTICKS) == 0)
|
|
uprintf("pp%d: printer out of paper\n", pp->pp_unit);
|
|
} else if ( ! PSSELECT(status) || PSERROR(status)) {
|
|
if ((pp->pp_notready++ % PPMSGTICKS) == 0)
|
|
uprintf("pp%d: printer not ready\n", pp->pp_unit);
|
|
} else if (--pp->pp_timer == 0) {
|
|
/*
|
|
* Timer has expired - see what's wrong.
|
|
*/
|
|
ppprint(DBTMOUT, ("pptimeout: status = 0x%x\n", status));
|
|
|
|
if (PSREADY(status)) {
|
|
/*
|
|
* We must have dropped an interrupt.
|
|
* If this is the first one we've dropped, assume
|
|
* it's a fluke and carry on. Otherwise, give up.
|
|
*/
|
|
if (pp->pp_lostintr++ == 0) {
|
|
ppprint(DBTMOUT, ("pptimeout: dropped intr\n"));
|
|
pp->pp_timer = PPTICKS; /* reset timer */
|
|
(void) ppintr();
|
|
} else {
|
|
printf("pp%d: not getting interrupts\n",
|
|
pp->pp_unit);
|
|
error = 1;
|
|
}
|
|
} else {
|
|
/* printer is hung */
|
|
error = 1;
|
|
}
|
|
}
|
|
|
|
if ( ! error) {
|
|
timeout(pptimeout, (caddr_t)pp, PPWATCHDOG*hz);
|
|
} else {
|
|
pp->pp_bp->b_flags |= B_ERROR;
|
|
ppiodone(pp);
|
|
pp->pp_flags &= ~PP_TIMER;
|
|
}
|
|
|
|
(void) splx(s);
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
ppioctl(dev, cmd, data, flag)
|
|
dev_t dev;
|
|
int cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
{
|
|
register struct ppstate *pp = &ppstate[PPUNIT(dev)];
|
|
unsigned char *udata;
|
|
|
|
udata = (unsigned char *) data;
|
|
switch(cmd) {
|
|
case PPIOCGETS:
|
|
*udata = PPREG_STAT;
|
|
break;
|
|
case PPIOCGETC:
|
|
*udata = PPREG_CTRL;
|
|
break;
|
|
case PPIOCSETC:
|
|
PPREG_CTRL = *udata;
|
|
break;
|
|
default:
|
|
return(ENOTTY);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* ppiowait:
|
|
* Private version of 'biowait()'.
|
|
*/
|
|
ppiowait(pp, bp)
|
|
struct ppstate *pp;
|
|
register struct buf *bp;
|
|
{
|
|
int s;
|
|
|
|
s = splx(pritospl(pp->pp_md->md_intpri));
|
|
while ( ! (bp->b_flags&B_DONE)) {
|
|
if (sleep((caddr_t)bp, PPPRI|PCATCH)) {
|
|
bp->b_flags |= (B_ERROR|B_DONE);
|
|
bp->b_error = EINTR;
|
|
}
|
|
}
|
|
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* ppiodone:
|
|
* Private version of 'biodone()'.
|
|
*/
|
|
ppiodone(pp)
|
|
register struct ppstate *pp;
|
|
{
|
|
register struct buf *bp = pp->pp_bp;
|
|
|
|
bp->b_flags |= B_DONE;
|
|
wakeup((caddr_t)bp);
|
|
|
|
pp->pp_flags &= ~PP_BUSY;
|
|
}
|
|
|
|
#if !defined(SUN386)
|
|
/*
|
|
* Handle a polling Parallel Printer interrupt.
|
|
*/
|
|
pppoll()
|
|
{
|
|
int serviced = 0;
|
|
|
|
/* Were we expecting an interrupt?
|
|
*/
|
|
ppprint(DBINTR, ("pppoll routine\n"));
|
|
ppstate[0].pp_regbase->pp_intr = 0;
|
|
ppprint(DBINTR, ("pppoll cleared interrupt\n"));
|
|
if (ppstate[0].pp_flags & PP_BUSY) {
|
|
serviced = ppintr();
|
|
}
|
|
return(serviced);
|
|
}
|
|
#endif
|
|
|
|
#endif NPP
|