/* * 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. * * $Id: lpa.c,v 1.5 1993/08/28 00:11:33 brezak Exp $ */ /* * Device Driver for AT parallel printer port * Written by William Jolitz 12/18/90 * Modified to run without interrupts * 92-08-19 Wolfgang Stanglmeier * Slight cleanup and reorganization, try to handle restarted syscalls * 92-09-08 Andy Valencia * 93-04-08 Rodney W. Grimes * Converted to driver name lpa, shares lptreg.h with lpt.c, fixed probe * so that it returns IO_LPTSIZE. Added dummy lpaioctl. Added my new * probe code from lpt.c, as the one in here was crud. */ #include "lpa.h" #if NLPA > 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.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 LPA_PRIME (0x20) /* prime printer on open */ #define LPA_ERROR (0x10) /* log error conditions */ #define LPA_FLAG(x) ((x) & 0xfc) #define LPA_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) /* debug flags */ #ifndef DEBUG #define lprintf #else #define lprintf if (lpaflag) printf int lpaflag = 1; #endif int lpaprobe(), lpaattach(); struct isa_driver lpadriver = {lpaprobe, lpaattach, "lpa"}; /* * 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 */ struct lpa_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; } lpa_sc[NLPA]; /* * Internal routine to lpaprobe to do port tests of one byte value */ int lpa_port_test(short port, u_char data, u_char mask) { int temp, timeout; data = data & mask; outb(port, data); timeout = 100; do temp = inb(port) & mask; while (temp != data && --timeout); lprintf("Port 0x%x\tout=%x\tin=%x\n", port, data, temp); return (temp == data); } /* * New lpaprobe routine written by Rodney W. Grimes, 3/25/1993 * * Logic: * 1) You should be able to write to and read back the same value * to the data port. Do an alternating zeros, alternating ones, * walking zero, and walking one test to check for stuck bits. * * 2) You should be able to write to and read back the same value * to the control port lower 5 bits, the upper 3 bits are reserved * per the IBM PC technical reference manauls and different boards * do different things with them. Do an alternating zeros, alternating * ones, walking zero, and walking one test to check for stuck bits. * * Some printers drag the strobe line down when the are powered off * so this bit has been masked out of the control port test. * * XXX Some printers may not like a fast pulse on init or strobe, I * don't know at this point, if that becomes a problem these bits * should be turned off in the mask byte for the control port test. * * 3) Set the data and control ports to a value of 0 */ int lpaprobe(struct isa_device *dvp) { int status; short port; u_char data; u_char mask; int i; status = IO_LPTSIZE; port = dvp->id_iobase + lpt_data; mask = 0xff; while (mask != 0) { data = 0x55; /* Alternating zeros */ if (!lpa_port_test(port, data, mask)) status = 0; data = 0xaa; /* Alternating ones */ if (!lpa_port_test(port, data, mask)) status = 0; for (i = 0; i < 8; i++) /* Walking zero */ { data = ~(1 << i); if (!lpa_port_test(port, data, mask)) status = 0; } for (i = 0; i < 8; i++) /* Walking one */ { data = (1 << i); if (!lpa_port_test(port, data, mask)) status = 0; } if (port == dvp->id_iobase + lpt_data) { port = dvp->id_iobase + lpt_control; mask = 0x1e; } else mask = 0; } outb(dvp->id_iobase+lpt_data, 0); outb(dvp->id_iobase+lpt_control, 0); return (status); } /* * lpaattach() * Install device */ lpaattach(isdp) struct isa_device *isdp; { struct lpa_softc *sc; sc = lpa_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); } /* * lpaopen() * New open on device. * * We forbid all but first open */ lpaopen(dev, flag) dev_t dev; int flag; { struct lpa_softc *sc; int delay; /* slept time in 1/hz seconds of tsleep */ int err; u_char sta, unit; unit= LPA_UNIT(minor(dev)); sta = LPA_FLAG(minor(dev)); /* minor number out of limits ? */ if (unit >= NLPA) return (ENXIO); sc = lpa_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 & LPA_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 ((caddr_t)sc, LPPRI, "lpa: 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 lpa_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((caddr_t)sc, LPPRI, "lpa: 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_smaxsc_smax++; } if (spin*2 < sc->sc_smax) { sc->sc_smax--; } } return(0); } /* * lpaclose() * Close on lp. Try to flush data in buffer out. */ lpaclose(dev, flag) dev_t dev; int flag; { struct lpa_softc *sc = lpa_sc + LPA_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); } /* * lpawrite() * Copy from user's buffer, then print */ lpawrite(dev, uio) dev_t dev; struct uio *uio; { struct lpa_softc *sc = lpa_sc + LPA_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); } int lpaioctl(dev_t dev, int cmd, caddr_t data, int flag) { int error; error = 0; switch (cmd) { #ifdef THISISASAMPLE case XXX: dothis; andthis; andthat; error=x; break; #endif /* THISISASAMPLE */ default: error = ENODEV; } return(error); } #endif /* NLPA > 0 */