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 */