Files
Arquivotheca.SunOS-4.1.4/sys/sundev/pp.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

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