Return to BSD News archive
Path: sserve!manuel!munnari.oz.au!spool.mu.edu!agate!ames!data.nas.nasa.gov!taligent!apple!motcsd!xhost92.csd.mot.com!ajv From: ajv@xhost92.csd.mot.com Newsgroups: comp.unix.bsd Subject: Re: replacement for lpt.c without interrupts Message-ID: <7070@motcsd.csd.mot.com> Date: 9 Sep 92 06:00:18 GMT References: <1992Aug11.201127.1600@jbsys.com>,<PCG.92Aug6161828@aberdb.aber.ac.uk> <1992Sep6.143703.5848@dentaro.GUN.de> Sender: usenet@motcsd.csd.mot.com Lines: 389 wolf@dentaro.GUN.de (Wolfgang Stanglmeier) writes: >I hacked the 386bsd LPT-driver to work without interrupts. >The busy loop timeout value adapts automatically to >the printer and system speed. <excellent driver follows...> I was working on something very much along the same lines. However, Mr. Stanglmeier hit the nail right on the head first. I have rolled in some of my other work on handling syscall restarts, and in the process applied my BSD-kernel style of indentation. What follows is substantially the same driver, with my own minor improvements. Kudos, Mr. Stanglmeier! My printer thanks you. :-) Andy Valencia ajv@csd.mot.com (for now) jtk@netcom.com (if it bounces) /* * Copyright (c) 1990 William F. Jolitz, TeleMuse * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This software is a component of "386BSD" developed by * William F. Jolitz, TeleMuse. * 4. Neither the name of the developer nor the name "386BSD" * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT * NOT MAKE USE OF THIS WORK. * * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * Device Driver for AT parallel printer port * Written by William Jolitz 12/18/90 * Modified to run without interrupts * 92-08-19 Wolfgang Stanglmeier <wolf@dentaro.GUN.de> * Slight cleanup and reorganization, try to handle restarted syscalls * 92-09-08 Andy Valencia <jtk@netcom.com> */ #include "lpt.h" #if NLPT > 0 #include "param.h" #include "buf.h" #include "systm.h" #include "ioctl.h" #include "tty.h" #include "proc.h" #include "user.h" #include "uio.h" #include "kernel.h" #include "malloc.h" #include "i386/isa/isa_device.h" #include "i386/isa/lptreg.h" /* internal used flags */ #define OPEN (0x01) /* device is open */ #define INIT (0x02) /* device in open procedure */ /* flags from minor device */ #define LPT_PRIME (0x20) /* prime printer on open */ #define LPT_ERROR (0x10) /* log error conditions */ #define LPT_FLAG(x) ((x) & 0xfc) #define LPT_UNIT(x) ((x) & 0x03) /* Printer Ready condition */ #define LPS_INVERT (LPS_NBSY | LPS_NACK | LPS_SEL | LPS_NERR) #define LPS_MASK (LPS_NBSY | LPS_NACK | LPS_OUT | LPS_SEL | LPS_NERR) #define NOT_READY() ((inb(sc->sc_stat)^LPS_INVERT)&LPS_MASK) /* tsleep priority */ #define LPPRI ((PZERO+8) | PCATCH) int lptprobe(), lptattach(); struct isa_driver lptdriver = {lptprobe, lptattach, "lpt"}; /* * copy usermode data into sysmode buffer */ #define BUFSIZE 1024 /* ** Waittimes */ #define TIMEOUT (hz*16) /* Timeout while open device */ #define LONG (hz* 1) /* Timesteps while open */ #define MAX_SPIN 255 /* max loop counter for busy wait */ /* Valid Controlbits for probe ... ** ** The lower 5 bits of controlport should be ** readable and writable, ** ** .... but if my deskjet is power down, it clobbers ** some lines, and the port will not be configured. ** So I mask them out */ #define LPC_MASK (0xfa) struct lpt_softc { char *sc_cp; /* current data to print */ int sc_count; /* bytes queued in sc_inbuf */ short sc_data; /* printer data port */ short sc_stat; /* printer control port */ short sc_ctrl; /* printer status port */ u_char sc_flags; /* flags (open and internal) */ u_char sc_unit; /* unit-number */ u_char sc_smax; /* current max busy loop cnt */ char /* buffer for data */ *sc_inbuf; } lpt_sc[NLPT]; /* In fact, I need no interrupt, but how can I explain it to config ??? */ lptintr(unit) int unit; { /* dummy */ ; } /* * lptprobe() * Probe for hardware */ lptprobe(idp) struct isa_device *idp; { unsigned v, w, n = 0; /* status */ do { if (++n >= 4) return (0); /* * Status port should be read only, * so readback value may not change */ outb(idp->id_iobase+lpt_status,0xf0); v = inb(idp->id_iobase+lpt_status); outb(idp->id_iobase+lpt_status,0); w = inb(idp->id_iobase+lpt_status); } while (v != w); /* control: the lower 5 bits of controlport should read back */ outb(idp->id_iobase+lpt_control,0xff); DELAY(100); w = inb(idp->id_iobase+lpt_control); if ((w ^ 0xff) & LPC_MASK) return(0); outb(idp->id_iobase+lpt_control,0); DELAY(100); w = inb(idp->id_iobase+lpt_control); if ((w ^ 0xe0) & LPC_MASK) return(0); return(1); } /* * lptattach() * Install device */ lptattach(isdp) struct isa_device *isdp; { struct lpt_softc *sc; sc = lpt_sc + isdp->id_unit; sc->sc_unit = isdp->id_unit; sc->sc_data = isdp->id_iobase + lpt_data; sc->sc_stat = isdp->id_iobase + lpt_status; sc->sc_ctrl = isdp->id_iobase + lpt_control; outb(sc->sc_ctrl, LPC_NINIT); return (1); } /* * lptopen() * New open on device. * * We forbid all but first open */ lptopen(dev, flag) dev_t dev; int flag; { struct lpt_softc *sc; int delay; /* slept time in 1/hz seconds of tsleep */ int err; u_char sta, unit; unit= LPT_UNIT(minor(dev)); sta = LPT_FLAG(minor(dev)); /* minor number out of limits ? */ if (unit >= NLPT) return (ENXIO); sc = lpt_sc + unit; /* Attached ? */ if (!sc->sc_ctrl) { /* not attached */ return(ENXIO); } /* Printer busy ? */ if (sc->sc_flags) { /* too late .. */ return(EBUSY); } /* Have memory for buffer? */ sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK); if (sc->sc_inbuf == 0) return(ENOMEM); /* Init printer */ sc->sc_flags = sta | INIT; if (sc->sc_flags & LPT_PRIME) { outb(sc->sc_ctrl, 0); } /* Select printer */ outb(sc->sc_ctrl, LPC_SEL|LPC_NINIT); /* and wait for ready .. */ for (delay=0; NOT_READY(); delay+= LONG) { if (delay >= TIMEOUT) { /* too long waited .. */ sc->sc_flags = 0; return (EBUSY); } /* sleep a moment */ if ((err = tsleep (sc, LPPRI, "lpt: open", LONG)) != EWOULDBLOCK) { sc->sc_flags = 0; return (EBUSY); } } /* Printer ready .. set variables */ sc->sc_flags |= OPEN; sc->sc_count = 0; return(0); } /* * pushbytes() * Workhorse for actually spinning and writing bytes to printer */ static pushbytes(sc) struct lpt_softc *sc; { int spin, err, tic; char ch; /* loop for every character .. */ while (sc->sc_count > 0) { /* printer data */ ch = *(sc->sc_cp); sc->sc_cp += 1; sc->sc_count -= 1; outb(sc->sc_data, ch); /* Busy wait for printer ready .. */ spin = tic = 0; while (NOT_READY()) { if (++spin >= sc->sc_smax) { /* * Now sleep, every cycle a * little longer .. */ tic = tic + tic + 1; err = tsleep(sc, LPPRI, "lpt: write", tic); if (err != EWOULDBLOCK) { return (err); } } } /* strobe */ outb(sc->sc_ctrl, LPC_NINIT|LPC_SEL|LPC_STB); outb(sc->sc_ctrl, LPC_NINIT|LPC_SEL); /* Adapt busy-wait length... */ if (spin >= sc->sc_smax) { /* was sleep wait */ if (sc->sc_smax<MAX_SPIN) sc->sc_smax++; } if (spin*2 < sc->sc_smax) { sc->sc_smax--; } } return(0); } /* * lptclose() * Close on lp. Try to flush data in buffer out. */ lptclose(dev, flag) dev_t dev; int flag; { struct lpt_softc *sc = lpt_sc + LPT_UNIT(minor(dev)); /* If there's queued data, try to flush it */ (void)pushbytes(sc); /* really close .. quite simple :-) */ outb(sc->sc_ctrl, LPC_NINIT); sc->sc_flags = 0; free(sc->sc_inbuf, M_DEVBUF); sc->sc_inbuf = 0; /* Sanity */ return(0); } /* * lptwrite() * Copy from user's buffer, then print */ lptwrite(dev, uio) dev_t dev; struct uio *uio; { struct lpt_softc *sc = lpt_sc + LPT_UNIT(minor(dev)); int err; /* Write out old bytes from interrupted syscall */ if (sc->sc_count > 0) { err = pushbytes(sc); if (err) return(err); } /* main loop */ while ((sc->sc_count = MIN(BUFSIZE, uio->uio_resid)) > 0) { /* get from user-space */ sc->sc_cp = sc->sc_inbuf; uiomove(sc->sc_inbuf, sc->sc_count, uio); err = pushbytes(sc); if (err) return(err); } return(0); } #endif /* NLP > 0 */