438 lines
11 KiB
C
438 lines
11 KiB
C
/*
|
|
* 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.6 1993/12/20 09:06:19 mycroft 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 <wolf@dentaro.GUN.de>
|
|
* Slight cleanup and reorganization, try to handle restarted syscalls
|
|
* 92-09-08 Andy Valencia <jtk@netcom.com>
|
|
* 93-04-08 Rodney W. Grimes <rgrimes@agora.rain.com>
|
|
* 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 <sys/param.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include <machine/pio.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_smax<MAX_SPIN)
|
|
sc->sc_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 */
|