#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 #include /* register definitions */ #if NPP > 0 #include #include #include #include #include #include #include /* * 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