New directory and drivers for Parallel Port Bus devices.
Submitted by: Nicolas Souchu <Nicolas.Souchu@prism.uvsq.fr>
This commit is contained in:
parent
fb35546309
commit
ed3815224c
765
sys/dev/ppbus/nlpt.c
Normal file
765
sys/dev/ppbus/nlpt.c
Normal file
@ -0,0 +1,765 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* from: unknown origin, 386BSD 0.1
|
||||
* From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Device Driver for AT parallel printer port
|
||||
* Written by William Jolitz 12/18/90
|
||||
*/
|
||||
|
||||
/*
|
||||
* Updated for ppbus by Nicolas Souchu
|
||||
* [Mon Jul 28 1997]
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/malloc.h>
|
||||
|
||||
#include <machine/stdarg.h>
|
||||
#include <machine/clock.h>
|
||||
#include <machine/lpt.h>
|
||||
|
||||
#include <i386/isa/isa.h>
|
||||
#include <i386/isa/isa_device.h>
|
||||
|
||||
#include <sys/kernel.h>
|
||||
#endif /*KERNEL */
|
||||
|
||||
#include <dev/ppbus/ppbconf.h>
|
||||
#include <dev/ppbus/nlpt.h>
|
||||
|
||||
#ifndef DEBUG
|
||||
#define nlprintf (void)
|
||||
#else
|
||||
#define nlprintf if (nlptflag) printf
|
||||
static int volatile nlptflag = 1;
|
||||
#endif
|
||||
|
||||
#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
|
||||
#define LPTOUTINITIAL 10 /* initial timeout to wait for ready 1/10 s */
|
||||
#define LPTOUTMAX 1 /* maximal timeout 1 s */
|
||||
#define LPPRI (PZERO+8)
|
||||
#define BUFSIZE 1024
|
||||
|
||||
#define LPTUNIT(s) ((s)&0x03)
|
||||
#define LPTFLAGS(s) ((s)&0xfc)
|
||||
|
||||
static int nlpt = 0;
|
||||
#define MAXLPT 8 /* XXX not much better! */
|
||||
static struct lpt_data *lptdata[MAXLPT];
|
||||
|
||||
/*
|
||||
* Make ourselves visible as a ppbus driver
|
||||
*/
|
||||
|
||||
static struct ppb_device *nlptprobe(struct ppb_data *ppb);
|
||||
static int nlptattach(struct ppb_device *dev);
|
||||
static void nlptintr(int unit);
|
||||
|
||||
#ifdef KERNEL
|
||||
|
||||
static struct ppb_driver nlptdriver = {
|
||||
nlptprobe, nlptattach, "nlpt"
|
||||
};
|
||||
DATA_SET(ppbdriver_set, nlptdriver);
|
||||
|
||||
#endif /* KERNEL */
|
||||
|
||||
/* bits for state */
|
||||
#define OPEN (1<<0) /* device is open */
|
||||
#define ASLP (1<<1) /* awaiting draining of printer */
|
||||
#define EERROR (1<<2) /* error was received from printer */
|
||||
#define OBUSY (1<<3) /* printer is busy doing output */
|
||||
#define LPTOUT (1<<4) /* timeout while not selected */
|
||||
#define TOUT (1<<5) /* timeout while not selected */
|
||||
#define INIT (1<<6) /* waiting to initialize for open */
|
||||
#define INTERRUPTED (1<<7) /* write call was interrupted */
|
||||
|
||||
|
||||
/* status masks to interrogate printer status */
|
||||
#define RDY_MASK (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR) /* ready ? */
|
||||
#define LP_READY (LPS_SEL|LPS_NBSY|LPS_NERR)
|
||||
|
||||
/* Printer Ready condition - from lpa.c */
|
||||
/* Only used in polling code */
|
||||
#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(lpt) ((ppb_rstr(&(lpt)->lpt_dev)^LPS_INVERT)&LPS_MASK)
|
||||
|
||||
#define MAX_SLEEP (hz*5) /* Timeout while waiting for device ready */
|
||||
#define MAX_SPIN 20 /* Max delay for device ready in usecs */
|
||||
|
||||
static d_open_t nlptopen;
|
||||
static d_close_t nlptclose;
|
||||
static d_write_t nlptwrite;
|
||||
static d_ioctl_t nlptioctl;
|
||||
|
||||
#define CDEV_MAJOR 16
|
||||
static struct cdevsw nlpt_cdevsw =
|
||||
{ nlptopen, nlptclose, noread, nlptwrite, /*16*/
|
||||
nlptioctl, nullstop, nullreset, nodevtotty,/* lpt */
|
||||
seltrue, nommap, nostrat, "nlpt", NULL, -1 };
|
||||
|
||||
/*
|
||||
* Internal routine to nlptprobe to do port tests of one byte value
|
||||
*/
|
||||
static int
|
||||
nlpt_port_test(struct lpt_data *lpt, u_char data, u_char mask)
|
||||
{
|
||||
int temp, timeout;
|
||||
|
||||
data = data & mask;
|
||||
ppb_wdtr(&lpt->lpt_dev, data);
|
||||
timeout = 10000;
|
||||
do {
|
||||
DELAY(10);
|
||||
temp = ppb_rdtr(&lpt->lpt_dev) & mask;
|
||||
}
|
||||
while (temp != data && --timeout);
|
||||
nlprintf("out=%x\tin=%x\ttout=%d\n", data, temp, timeout);
|
||||
return (temp == data);
|
||||
}
|
||||
|
||||
/*
|
||||
* New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94
|
||||
* Based partially on Rod Grimes' printer probe
|
||||
*
|
||||
* Logic:
|
||||
* 1) If no port address was given, use the bios detected ports
|
||||
* and autodetect what ports the printers are on.
|
||||
* 2) Otherwise, probe the data port at the address given,
|
||||
* using the method in Rod Grimes' port probe.
|
||||
* (Much code ripped off directly from Rod's probe.)
|
||||
*
|
||||
* Comments from Rod's probe:
|
||||
* 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.
|
||||
*
|
||||
* We are finally left with a mask of 0x14, due to some printers
|
||||
* being adamant about holding other bits high ........
|
||||
*
|
||||
* Before probing the control port, we write a 0 to the data port -
|
||||
* If not, some printers chuck out garbage when the strobe line
|
||||
* gets toggled.
|
||||
*
|
||||
* 3) Set the data and control ports to a value of 0
|
||||
*
|
||||
* This probe routine has been tested on Epson Lx-800, HP LJ3P,
|
||||
* Epson FX-1170 and C.Itoh 8510RM
|
||||
* printers.
|
||||
* Quick exit on fail added.
|
||||
*/
|
||||
static int
|
||||
nlpt_detect(struct lpt_data *lpt)
|
||||
{
|
||||
int status;
|
||||
u_char data;
|
||||
u_char mask;
|
||||
int i;
|
||||
|
||||
status = IO_LPTSIZE;
|
||||
|
||||
if (ppb_request_bus(&lpt->lpt_dev, PPB_DONTWAIT)) {
|
||||
printf("nlpt: cannot alloc ppbus!\n");
|
||||
status = 0 ; goto end_probe ;
|
||||
}
|
||||
|
||||
mask = 0xff;
|
||||
data = 0x55; /* Alternating zeros */
|
||||
if (!nlpt_port_test(lpt, data, mask))
|
||||
{ status = 0 ; goto end_probe ; }
|
||||
|
||||
data = 0xaa; /* Alternating ones */
|
||||
if (!nlpt_port_test(lpt, data, mask))
|
||||
{ status = 0 ; goto end_probe ; }
|
||||
|
||||
for (i = 0; i < 8; i++) { /* Walking zero */
|
||||
data = ~(1 << i);
|
||||
if (!nlpt_port_test(lpt, data, mask))
|
||||
{ status = 0 ; goto end_probe ; }
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) { /* Walking one */
|
||||
data = (1 << i);
|
||||
if (!nlpt_port_test(lpt, data, mask))
|
||||
{ status = 0 ; goto end_probe ; }
|
||||
}
|
||||
|
||||
end_probe:
|
||||
/* write 0's to control and data ports */
|
||||
ppb_wdtr(&lpt->lpt_dev, 0);
|
||||
ppb_wctr(&lpt->lpt_dev, 0);
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
/*
|
||||
* nlptprobe()
|
||||
*/
|
||||
static struct ppb_device *
|
||||
nlptprobe(struct ppb_data *ppb)
|
||||
{
|
||||
struct lpt_data *lpt;
|
||||
|
||||
lpt = (struct lpt_data *) malloc(sizeof(struct lpt_data),
|
||||
M_TEMP, M_NOWAIT);
|
||||
if (!lpt) {
|
||||
printf("nlpt: cannot malloc!\n");
|
||||
return (0);
|
||||
}
|
||||
bzero(lpt, sizeof(struct lpt_data));
|
||||
|
||||
lptdata[nlpt] = lpt;
|
||||
|
||||
/*
|
||||
* lpt dependent initialisation.
|
||||
*/
|
||||
lpt->lpt_unit = nlpt;
|
||||
|
||||
/*
|
||||
* ppbus dependent initialisation.
|
||||
*/
|
||||
lpt->lpt_dev.id_unit = lpt->lpt_unit;
|
||||
lpt->lpt_dev.ppb = ppb;
|
||||
lpt->lpt_dev.intr = nlptintr;
|
||||
|
||||
/*
|
||||
* Now, try to detect the printer.
|
||||
*/
|
||||
if (!nlpt_detect(lpt)) {
|
||||
free(lpt, M_TEMP);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Ok, go to next device on next probe */
|
||||
nlpt ++;
|
||||
|
||||
return (&lpt->lpt_dev);
|
||||
}
|
||||
|
||||
static int
|
||||
nlptattach(struct ppb_device *dev)
|
||||
{
|
||||
struct lpt_data *lpt = lptdata[dev->id_unit];
|
||||
|
||||
/*
|
||||
* Report ourselves
|
||||
*/
|
||||
printf("nlpt%d: <generic printer> on ppbus %d\n",
|
||||
dev->id_unit, dev->ppb->ppb_link->adapter_unit);
|
||||
|
||||
lpt->sc_primed = 0; /* not primed yet */
|
||||
|
||||
if (ppb_request_bus(&lpt->lpt_dev, PPB_DONTWAIT)) {
|
||||
printf("nlpt: cannot alloc ppbus!\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
ppb_wctr(&lpt->lpt_dev, LPC_NINIT);
|
||||
|
||||
/* check if we can use interrupt, should be done by ppc stuff */
|
||||
nlprintf("oldirq %x\n", lpt->sc_irq);
|
||||
if (ppb_get_irq(&lpt->lpt_dev)) {
|
||||
lpt->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
|
||||
printf("nlpt%d: Interrupt-driven port\n", dev->id_unit);
|
||||
} else {
|
||||
lpt->sc_irq = 0;
|
||||
nlprintf("nlpt%d: Polled port\n", dev->id_unit);
|
||||
}
|
||||
nlprintf("irq %x\n", lpt->sc_irq);
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
nlptout(struct lpt_data *lpt)
|
||||
{ int pl;
|
||||
|
||||
nlprintf ("T %x ", ppb_rstr(&lpt->lpt_dev));
|
||||
if (lpt->sc_state & OPEN) {
|
||||
lpt->sc_backoff++;
|
||||
if (lpt->sc_backoff > hz/LPTOUTMAX)
|
||||
lpt->sc_backoff = lpt->sc_backoff > hz/LPTOUTMAX;
|
||||
timeout((timeout_func_t)nlptout, (caddr_t)lpt, lpt->sc_backoff);
|
||||
} else
|
||||
lpt->sc_state &= ~TOUT;
|
||||
|
||||
if (lpt->sc_state & EERROR)
|
||||
lpt->sc_state &= ~EERROR;
|
||||
|
||||
/*
|
||||
* Avoid possible hangs do to missed interrupts
|
||||
*/
|
||||
if (lpt->sc_xfercnt) {
|
||||
pl = spltty();
|
||||
nlptintr(lpt->lpt_unit);
|
||||
splx(pl);
|
||||
} else {
|
||||
lpt->sc_state &= ~OBUSY;
|
||||
wakeup((caddr_t)lpt);
|
||||
}
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* nlptopen -- reset the printer, then wait until it's selected and not busy.
|
||||
* If LP_BYPASS flag is selected, then we do not try to select the
|
||||
* printer -- this is just used for passing ioctls.
|
||||
*/
|
||||
|
||||
static int
|
||||
nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
|
||||
{
|
||||
struct lpt_data *lpt;
|
||||
|
||||
int s;
|
||||
int trys;
|
||||
u_int unit = LPTUNIT(minor(dev));
|
||||
|
||||
if ((unit >= nlpt))
|
||||
return (ENXIO);
|
||||
|
||||
lpt = lptdata[unit];
|
||||
|
||||
if (ppb_request_bus(&lpt->lpt_dev, PPB_WAIT|PPB_INTR))
|
||||
return (EINTR);
|
||||
|
||||
if (lpt->sc_state) {
|
||||
nlprintf("nlpt: still open %x\n", lpt->sc_state);
|
||||
return(EBUSY);
|
||||
} else
|
||||
lpt->sc_state |= INIT;
|
||||
|
||||
lpt->sc_flags = LPTFLAGS(minor(dev));
|
||||
|
||||
/* Check for open with BYPASS flag set. */
|
||||
if (lpt->sc_flags & LP_BYPASS) {
|
||||
lpt->sc_state = OPEN;
|
||||
return(0);
|
||||
}
|
||||
|
||||
s = spltty();
|
||||
nlprintf("nlpt flags 0x%x\n", lpt->sc_flags);
|
||||
|
||||
/* set IRQ status according to ENABLE_IRQ flag */
|
||||
if (lpt->sc_irq & LP_ENABLE_IRQ)
|
||||
lpt->sc_irq |= LP_USE_IRQ;
|
||||
else
|
||||
lpt->sc_irq &= ~LP_USE_IRQ;
|
||||
|
||||
/* init printer */
|
||||
if ((lpt->sc_flags & LP_NO_PRIME) == 0) {
|
||||
if((lpt->sc_flags & LP_PRIMEOPEN) || lpt->sc_primed == 0) {
|
||||
ppb_wctr(&lpt->lpt_dev, 0);
|
||||
lpt->sc_primed++;
|
||||
DELAY(500);
|
||||
}
|
||||
}
|
||||
|
||||
ppb_wctr(&lpt->lpt_dev, LPC_SEL|LPC_NINIT);
|
||||
|
||||
/* wait till ready (printer running diagnostics) */
|
||||
trys = 0;
|
||||
do {
|
||||
/* ran out of waiting for the printer */
|
||||
if (trys++ >= LPINITRDY*4) {
|
||||
splx(s);
|
||||
lpt->sc_state = 0;
|
||||
nlprintf ("status %x\n", ppb_rstr(&lpt->lpt_dev) );
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
/* wait 1/4 second, give up if we get a signal */
|
||||
if (tsleep((caddr_t)lpt, LPPRI|PCATCH, "lptinit", hz/4) !=
|
||||
EWOULDBLOCK) {
|
||||
lpt->sc_state = 0;
|
||||
splx(s);
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
/* is printer online and ready for output */
|
||||
} while ((ppb_rstr(&lpt->lpt_dev) &
|
||||
(LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
|
||||
(LPS_SEL|LPS_NBSY|LPS_NERR));
|
||||
|
||||
lpt->sc_control = LPC_SEL|LPC_NINIT;
|
||||
if (lpt->sc_flags & LP_AUTOLF)
|
||||
lpt->sc_control |= LPC_AUTOL;
|
||||
|
||||
/* enable interrupt if interrupt-driven */
|
||||
if (lpt->sc_irq & LP_USE_IRQ)
|
||||
lpt->sc_control |= LPC_ENA;
|
||||
|
||||
ppb_wctr(&lpt->lpt_dev, lpt->sc_control);
|
||||
|
||||
lpt->sc_state = OPEN;
|
||||
lpt->sc_inbuf = geteblk(BUFSIZE);
|
||||
lpt->sc_xfercnt = 0;
|
||||
splx(s);
|
||||
|
||||
/* only use timeout if using interrupt */
|
||||
nlprintf("irq %x\n", lpt->sc_irq);
|
||||
if (lpt->sc_irq & LP_USE_IRQ) {
|
||||
lpt->sc_state |= TOUT;
|
||||
timeout((timeout_func_t)nlptout, (caddr_t)lpt,
|
||||
(lpt->sc_backoff = hz/LPTOUTINITIAL));
|
||||
} else
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
|
||||
nlprintf("opened.\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* nlptclose -- close the device, free the local line buffer.
|
||||
*
|
||||
* Check for interrupted write call added.
|
||||
*/
|
||||
|
||||
static int
|
||||
nlptclose(dev_t dev, int flags, int fmt, struct proc *p)
|
||||
{
|
||||
struct lpt_data *lpt = lptdata[LPTUNIT(minor(dev))];
|
||||
|
||||
if(lpt->sc_flags & LP_BYPASS)
|
||||
goto end_close;
|
||||
|
||||
if (ppb_request_bus(&lpt->lpt_dev, PPB_WAIT|PPB_INTR))
|
||||
return (EINTR);
|
||||
|
||||
lpt->sc_state &= ~OPEN;
|
||||
|
||||
/* if the last write was interrupted, don't complete it */
|
||||
if((!(lpt->sc_state & INTERRUPTED)) && (lpt->sc_irq & LP_USE_IRQ))
|
||||
while ((ppb_rstr(&lpt->lpt_dev) &
|
||||
(LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
|
||||
(LPS_SEL|LPS_NBSY|LPS_NERR) || lpt->sc_xfercnt)
|
||||
/* wait 1/4 second, give up if we get a signal */
|
||||
if (tsleep((caddr_t)lpt, LPPRI|PCATCH,
|
||||
"lpclose", hz) != EWOULDBLOCK)
|
||||
break;
|
||||
|
||||
ppb_wctr(&lpt->lpt_dev, LPC_NINIT);
|
||||
brelse(lpt->sc_inbuf);
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
|
||||
end_close:
|
||||
lpt->sc_state = 0;
|
||||
lpt->sc_xfercnt = 0;
|
||||
nlprintf("closed.\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* nlpt_pushbytes()
|
||||
* Workhorse for actually spinning and writing bytes to printer
|
||||
* Derived from lpa.c
|
||||
* Originally by ?
|
||||
*
|
||||
* This code is only used when we are polling the port
|
||||
*/
|
||||
static int
|
||||
nlpt_pushbytes(struct lpt_data *lpt)
|
||||
{
|
||||
int spin, err, tic;
|
||||
char ch;
|
||||
|
||||
nlprintf("p");
|
||||
/* loop for every character .. */
|
||||
while (lpt->sc_xfercnt > 0) {
|
||||
/* printer data */
|
||||
ch = *(lpt->sc_cp);
|
||||
lpt->sc_cp++;
|
||||
lpt->sc_xfercnt--;
|
||||
|
||||
/*
|
||||
* Wait for printer ready.
|
||||
* Loop 20 usecs testing BUSY bit, then sleep
|
||||
* for exponentially increasing timeout. (vak)
|
||||
*/
|
||||
for (spin = 0; NOT_READY(lpt) && spin < MAX_SPIN; ++spin)
|
||||
DELAY(1); /* XXX delay is NOT this accurate! */
|
||||
if (spin >= MAX_SPIN) {
|
||||
tic = 0;
|
||||
while (NOT_READY(lpt)) {
|
||||
/*
|
||||
* Now sleep, every cycle a
|
||||
* little longer ..
|
||||
*/
|
||||
tic = tic + tic + 1;
|
||||
/*
|
||||
* But no more than 10 seconds. (vak)
|
||||
*/
|
||||
if (tic > MAX_SLEEP)
|
||||
tic = MAX_SLEEP;
|
||||
err = tsleep((caddr_t)lpt, LPPRI,
|
||||
"lptpoll", tic);
|
||||
if (err != EWOULDBLOCK) {
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* output data */
|
||||
ppb_wdtr(&lpt->lpt_dev, ch);
|
||||
/* strobe */
|
||||
ppb_wctr(&lpt->lpt_dev, lpt->sc_control|LPC_STB);
|
||||
ppb_wctr(&lpt->lpt_dev, lpt->sc_control);
|
||||
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* nlptwrite --copy a line from user space to a local buffer, then call
|
||||
* putc to get the chars moved to the output queue.
|
||||
*
|
||||
* Flagging of interrupted write added.
|
||||
*/
|
||||
|
||||
static int
|
||||
nlptwrite(dev_t dev, struct uio *uio, int ioflag)
|
||||
{
|
||||
register unsigned n;
|
||||
int pl, err;
|
||||
struct lpt_data *lpt = lptdata[LPTUNIT(minor(dev))];
|
||||
|
||||
if(lpt->sc_flags & LP_BYPASS) {
|
||||
/* we can't do writes in bypass mode */
|
||||
return(EPERM);
|
||||
}
|
||||
|
||||
if (ppb_request_bus(&lpt->lpt_dev, PPB_WAIT|PPB_INTR))
|
||||
return (EINTR);
|
||||
|
||||
lpt->sc_state &= ~INTERRUPTED;
|
||||
while ((n = min(BUFSIZE, uio->uio_resid)) != 0) {
|
||||
lpt->sc_cp = lpt->sc_inbuf->b_un.b_addr ;
|
||||
uiomove(lpt->sc_cp, n, uio);
|
||||
lpt->sc_xfercnt = n ;
|
||||
while ((lpt->sc_xfercnt > 0)&&(lpt->sc_irq & LP_USE_IRQ)) {
|
||||
nlprintf("i");
|
||||
/* if the printer is ready for a char, */
|
||||
/* give it one */
|
||||
if ((lpt->sc_state & OBUSY) == 0){
|
||||
nlprintf("\nC %d. ", lpt->sc_xfercnt);
|
||||
pl = spltty();
|
||||
nlptintr(lpt->lpt_unit);
|
||||
(void) splx(pl);
|
||||
}
|
||||
nlprintf("W ");
|
||||
if (lpt->sc_state & OBUSY)
|
||||
if ((err = tsleep((caddr_t)lpt,
|
||||
LPPRI|PCATCH, "lpwrite", 0))) {
|
||||
lpt->sc_state |= INTERRUPTED;
|
||||
return(err);
|
||||
}
|
||||
}
|
||||
/* check to see if we must do a polled write */
|
||||
if(!(lpt->sc_irq & LP_USE_IRQ) && (lpt->sc_xfercnt)) {
|
||||
nlprintf("p");
|
||||
if((err = nlpt_pushbytes(lpt)))
|
||||
return(err);
|
||||
}
|
||||
|
||||
ppb_release_bus(&lpt->lpt_dev);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* nlptintr -- handle printer interrupts which occur when the printer is
|
||||
* ready to accept another char.
|
||||
*
|
||||
* do checking for interrupted write call.
|
||||
*/
|
||||
|
||||
void
|
||||
nlptintr(int unit)
|
||||
{
|
||||
struct lpt_data *lpt = lptdata[unit];
|
||||
int sts;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Is printer online and ready for output?
|
||||
*
|
||||
* Avoid falling back to nlptout() too quickly. First spin-loop
|
||||
* to see if the printer will become ready ``really soon now''.
|
||||
*/
|
||||
for (i = 0; i < 100 &&
|
||||
((sts=ppb_rstr(&lpt->lpt_dev)) & RDY_MASK) != LP_READY; i++) ;
|
||||
|
||||
if ((sts & RDY_MASK) == LP_READY) {
|
||||
lpt->sc_state = (lpt->sc_state | OBUSY) & ~EERROR;
|
||||
lpt->sc_backoff = hz/LPTOUTINITIAL;
|
||||
|
||||
if (lpt->sc_xfercnt) {
|
||||
/* send char */
|
||||
/*nlprintf("%x ", *lpt->sc_cp); */
|
||||
ppb_wdtr(&lpt->lpt_dev, *lpt->sc_cp++) ;
|
||||
ppb_wctr(&lpt->lpt_dev, lpt->sc_control|LPC_STB);
|
||||
/* DELAY(X) */
|
||||
ppb_wctr(&lpt->lpt_dev, lpt->sc_control);
|
||||
|
||||
/* any more data for printer */
|
||||
if(--(lpt->sc_xfercnt) > 0) return;
|
||||
}
|
||||
|
||||
/*
|
||||
* No more data waiting for printer.
|
||||
* Wakeup is not done if write call was interrupted.
|
||||
*/
|
||||
lpt->sc_state &= ~OBUSY;
|
||||
if(!(lpt->sc_state & INTERRUPTED))
|
||||
wakeup((caddr_t)lpt);
|
||||
nlprintf("w ");
|
||||
return;
|
||||
} else { /* check for error */
|
||||
if(((sts & (LPS_NERR | LPS_OUT) ) != LPS_NERR) &&
|
||||
(lpt->sc_state & OPEN))
|
||||
lpt->sc_state |= EERROR;
|
||||
/* nlptout() will jump in and try to restart. */
|
||||
}
|
||||
nlprintf("sts %x ", sts);
|
||||
}
|
||||
|
||||
static int
|
||||
nlptioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p)
|
||||
{
|
||||
int error = 0;
|
||||
struct lpt_data *lpt;
|
||||
u_int unit = LPTUNIT(minor(dev));
|
||||
u_char old_sc_irq; /* old printer IRQ status */
|
||||
|
||||
lpt = lptdata[unit];
|
||||
|
||||
switch (cmd) {
|
||||
case LPT_IRQ :
|
||||
if(lpt->sc_irq & LP_HAS_IRQ) {
|
||||
/*
|
||||
* NOTE:
|
||||
* If the IRQ status is changed,
|
||||
* this will only be visible on the
|
||||
* next open.
|
||||
*
|
||||
* If interrupt status changes,
|
||||
* this gets syslog'd.
|
||||
*/
|
||||
old_sc_irq = lpt->sc_irq;
|
||||
if(*(int*)data == 0)
|
||||
lpt->sc_irq &= (~LP_ENABLE_IRQ);
|
||||
else
|
||||
lpt->sc_irq |= LP_ENABLE_IRQ;
|
||||
if (old_sc_irq != lpt->sc_irq )
|
||||
log(LOG_NOTICE, "lpt%c switched to %s mode\n",
|
||||
(char)unit+'0',
|
||||
(lpt->sc_irq & LP_ENABLE_IRQ)?
|
||||
"interrupt-driven":"polled");
|
||||
} else /* polled port */
|
||||
error = EOPNOTSUPP;
|
||||
break;
|
||||
default:
|
||||
error = ENODEV;
|
||||
}
|
||||
|
||||
return(error);
|
||||
}
|
||||
|
||||
static nlpt_devsw_installed = 0;
|
||||
|
||||
static void nlpt_drvinit(void *unused)
|
||||
{
|
||||
dev_t dev;
|
||||
|
||||
if( ! nlpt_devsw_installed ) {
|
||||
dev = makedev(CDEV_MAJOR, 0);
|
||||
cdevsw_add(&dev,&nlpt_cdevsw, NULL);
|
||||
nlpt_devsw_installed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
SYSINIT(nlptdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nlpt_drvinit,NULL)
|
74
sys/dev/ppbus/nlpt.h
Normal file
74
sys/dev/ppbus/nlpt.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
|
||||
*
|
||||
* Distantly from :
|
||||
* @(#)lptreg.h 1.1 (Berkeley) 12/19/90
|
||||
* Id: lptreg.h,v 1.6 1997/02/22 09:36:52 peter Exp
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
#ifndef __NLPT_H
|
||||
#define __NLPT_H
|
||||
|
||||
#define LPS_NERR 0x08 /* printer no error */
|
||||
#define LPS_SEL 0x10 /* printer selected */
|
||||
#define LPS_OUT 0x20 /* printer out of paper */
|
||||
#define LPS_NACK 0x40 /* printer no ack of data */
|
||||
#define LPS_NBSY 0x80 /* printer no ack of data */
|
||||
|
||||
#define LPC_STB 0x01 /* strobe data to printer */
|
||||
#define LPC_AUTOL 0x02 /* automatic linefeed */
|
||||
#define LPC_NINIT 0x04 /* initialize printer */
|
||||
#define LPC_SEL 0x08 /* printer selected */
|
||||
#define LPC_ENA 0x10 /* enable IRQ */
|
||||
|
||||
struct lpt_data {
|
||||
unsigned short lpt_unit;
|
||||
|
||||
struct ppb_device lpt_dev;
|
||||
|
||||
short sc_state;
|
||||
/* default case: negative prime, negative ack, handshake strobe,
|
||||
prime once */
|
||||
u_char sc_control;
|
||||
char sc_flags;
|
||||
#define LP_POS_INIT 0x04 /* if we are a postive init signal */
|
||||
#define LP_POS_ACK 0x08 /* if we are a positive going ack */
|
||||
#define LP_NO_PRIME 0x10 /* don't prime the printer at all */
|
||||
#define LP_PRIMEOPEN 0x20 /* prime on every open */
|
||||
#define LP_AUTOLF 0x40 /* tell printer to do an automatic lf */
|
||||
#define LP_BYPASS 0x80 /* bypass printer ready checks */
|
||||
struct buf *sc_inbuf;
|
||||
short sc_xfercnt ;
|
||||
char sc_primed;
|
||||
char *sc_cp ;
|
||||
u_char sc_irq ; /* IRQ status of port */
|
||||
#define LP_HAS_IRQ 0x01 /* we have an irq available */
|
||||
#define LP_USE_IRQ 0x02 /* we are using our irq */
|
||||
#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */
|
||||
u_char sc_backoff ; /* time to call lptout() again */
|
||||
};
|
||||
|
||||
#endif
|
339
sys/dev/ppbus/ppbconf.c
Normal file
339
sys/dev/ppbus/ppbconf.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
|
||||
*
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#include <machine/clock.h>
|
||||
#include <machine/lpt.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <i386/isa/isa.h>
|
||||
#include <i386/isa/isa_device.h>
|
||||
|
||||
#include <dev/ppbus/ppbconf.h>
|
||||
|
||||
LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */
|
||||
|
||||
/*
|
||||
* Add a null driver so that the linker set always exists.
|
||||
*/
|
||||
|
||||
static struct ppb_driver nulldriver = {
|
||||
NULL, NULL, "null"
|
||||
};
|
||||
DATA_SET(ppbdriver_set, nulldriver);
|
||||
|
||||
|
||||
/*
|
||||
* Parallel Port Bus sleep/wakeup queue.
|
||||
*/
|
||||
#define PRIPPB 28 /* PSOCK < PRIPPB < PWAIT XXX */
|
||||
|
||||
/*
|
||||
* ppb_alloc_bus()
|
||||
*
|
||||
* Allocate area to store the ppbus description.
|
||||
* This function is called by ppcattach().
|
||||
*/
|
||||
struct ppb_data *
|
||||
ppb_alloc_bus(void)
|
||||
{
|
||||
struct ppb_data *ppb;
|
||||
static int ppbdata_initted = 0; /* done-init flag */
|
||||
|
||||
ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data),
|
||||
M_TEMP, M_NOWAIT);
|
||||
|
||||
/*
|
||||
* Add the new parallel port bus to the list of existing ppbus.
|
||||
*/
|
||||
if (ppb) {
|
||||
bzero(ppb, sizeof(struct ppb_data));
|
||||
|
||||
if (!ppbdata_initted) { /* list not initialised */
|
||||
LIST_INIT(&ppbdata);
|
||||
ppbdata_initted = 1;
|
||||
}
|
||||
LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain);
|
||||
} else {
|
||||
printf("ppb_alloc_bus: cannot malloc!\n");
|
||||
}
|
||||
return(ppb);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_attachdevs()
|
||||
*
|
||||
* Called by ppcattach(), this function probes the ppbus and
|
||||
* attaches found devices.
|
||||
*/
|
||||
int
|
||||
ppb_attachdevs(struct ppb_data *ppb)
|
||||
{
|
||||
int error;
|
||||
struct ppb_device *dev;
|
||||
struct ppb_driver **p_drvpp, *p_drvp;
|
||||
|
||||
LIST_INIT(&ppb->ppb_devs); /* initialise device/driver list */
|
||||
p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
|
||||
|
||||
/*
|
||||
* Blindly try all probes here. Later we should look at
|
||||
* the parallel-port PnP standard, and intelligently seek
|
||||
* drivers based on configuration first.
|
||||
*/
|
||||
while ((p_drvp = *p_drvpp++) != NULL) {
|
||||
if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
|
||||
/*
|
||||
* Add the device to the list of probed devices.
|
||||
*/
|
||||
LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
|
||||
|
||||
/* Call the device's attach routine */
|
||||
(void)p_drvp->attach(dev);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_request_bus()
|
||||
*
|
||||
* Allocate the device to perform transfers.
|
||||
*
|
||||
* how : PPB_WAIT or PPB_DONTWAIT
|
||||
*/
|
||||
int
|
||||
ppb_request_bus(struct ppb_device *dev, int how)
|
||||
{
|
||||
int s, error = 0;
|
||||
struct ppb_data *ppb = dev->ppb;
|
||||
|
||||
/*
|
||||
* During initialisation, ppb is null.
|
||||
*/
|
||||
if (!ppb)
|
||||
return (0);
|
||||
|
||||
while (error != EINTR) {
|
||||
s = splhigh();
|
||||
if (ppb->ppb_owner) {
|
||||
splx(s);
|
||||
|
||||
switch (how) {
|
||||
case (PPB_WAIT | PPB_INTR):
|
||||
|
||||
error = tsleep(ppb, PRIPPB | PCATCH,
|
||||
"ppbreq", 0);
|
||||
break;
|
||||
case (PPB_WAIT):
|
||||
error = tsleep(ppb, PRIPPB, "ppbreq", 0);
|
||||
break;
|
||||
default:
|
||||
return EWOULDBLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
ppb->ppb_owner = dev;
|
||||
|
||||
splx(s);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (EINTR);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_release_bus()
|
||||
*
|
||||
* Release the device allocated with ppb_request_dev()
|
||||
*/
|
||||
int
|
||||
ppb_release_bus(struct ppb_device *dev)
|
||||
{
|
||||
int s;
|
||||
struct ppb_data *ppb = dev->ppb;
|
||||
|
||||
/*
|
||||
* During initialisation, ppb is null.
|
||||
*/
|
||||
if (!ppb)
|
||||
return (0);
|
||||
|
||||
s = splhigh();
|
||||
if (ppb->ppb_owner != dev) {
|
||||
splx(s);
|
||||
return (EACCES);
|
||||
}
|
||||
|
||||
ppb->ppb_owner = 0;
|
||||
splx(s);
|
||||
|
||||
/*
|
||||
* Wakeup waiting processes.
|
||||
*/
|
||||
wakeup(ppb);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_intr()
|
||||
*
|
||||
* Function called by ppcintr() when an intr occurs.
|
||||
*/
|
||||
void
|
||||
ppb_intr(struct ppb_link *pl)
|
||||
{
|
||||
struct ppb_data *ppb = pl->ppbus;
|
||||
|
||||
/*
|
||||
* Call chipset dependent code.
|
||||
* Should be filled at chipset initialisation if needed.
|
||||
*/
|
||||
if (pl->adapter->intr_handler)
|
||||
(*pl->adapter->intr_handler)(pl->adapter_unit);
|
||||
|
||||
/*
|
||||
* Call upper handler iff the bus is owned by a device and
|
||||
* this device has specified an interrupt handler.
|
||||
*/
|
||||
if (ppb->ppb_owner && ppb->ppb_owner->intr)
|
||||
(*ppb->ppb_owner->intr)(ppb->ppb_owner->id_unit);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_reset_epp_timeout()
|
||||
*
|
||||
* Reset the EPP timeout bit in the status register.
|
||||
*/
|
||||
int
|
||||
ppb_reset_epp_timeout(struct ppb_device *dev)
|
||||
{
|
||||
struct ppb_data *ppb = dev->ppb;
|
||||
|
||||
if (ppb->ppb_owner != dev)
|
||||
return (EACCES);
|
||||
|
||||
(*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_ecp_sync()
|
||||
*
|
||||
* Wait for the ECP FIFO to be empty.
|
||||
*/
|
||||
int
|
||||
ppb_ecp_sync(struct ppb_device *dev)
|
||||
{
|
||||
struct ppb_data *ppb = dev->ppb;
|
||||
|
||||
if (ppb->ppb_owner != dev)
|
||||
return (EACCES);
|
||||
|
||||
(*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_get_mode()
|
||||
*
|
||||
* Read the mode (SPP, EPP...) of the chipset.
|
||||
*/
|
||||
int
|
||||
ppb_get_mode(struct ppb_device *dev)
|
||||
{
|
||||
return (dev->ppb->ppb_link->mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_get_epp_protocol()
|
||||
*
|
||||
* Read the EPP protocol (1.9 or 1.7).
|
||||
*/
|
||||
int
|
||||
ppb_get_epp_protocol(struct ppb_device *dev)
|
||||
{
|
||||
return (dev->ppb->ppb_link->epp_protocol);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_get_irq()
|
||||
*
|
||||
* Return the irq, 0 if none.
|
||||
*/
|
||||
int
|
||||
ppb_get_irq(struct ppb_device *dev)
|
||||
{
|
||||
return (dev->ppb->ppb_link->id_irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* ppb_get_status()
|
||||
*
|
||||
* Read the status register and update the status info.
|
||||
*/
|
||||
int
|
||||
ppb_get_status(struct ppb_device *dev, struct ppb_status *status)
|
||||
{
|
||||
struct ppb_data *ppb = dev->ppb;
|
||||
register char r;
|
||||
|
||||
if (ppb->ppb_owner != dev)
|
||||
return (EACCES);
|
||||
|
||||
r = status->status = ppb_rstr(dev);
|
||||
|
||||
status->timeout = r & TIMEOUT;
|
||||
status->error = !(r & nFAULT);
|
||||
status->select = r & SELECT;
|
||||
status->paper_end = r & ERROR;
|
||||
status->ack = !(r & nACK);
|
||||
status->busy = !(r & nBUSY);
|
||||
|
||||
return (0);
|
||||
}
|
239
sys/dev/ppbus/ppbconf.h
Normal file
239
sys/dev/ppbus/ppbconf.h
Normal file
@ -0,0 +1,239 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
|
||||
*
|
||||
*/
|
||||
#ifndef __PPBCONF_H
|
||||
#define __PPBCONF_H
|
||||
|
||||
/*
|
||||
* Parallel Port Chipset modes.
|
||||
*/
|
||||
#define PPB_AUTODETECT 0x0 /* autodetect */
|
||||
#define PPB_NIBBLE 0x1 /* standard 4 bit mode */
|
||||
#define PPB_PS2 0x2 /* PS/2 byte mode */
|
||||
#define PPB_EPP 0x3 /* EPP mode, 32 bit */
|
||||
#define PPB_ECP_EPP 0x4 /* ECP in EPP mode */
|
||||
#define PPB_ECP_PS2 0x5 /* ECP in PS/2 mode */
|
||||
#define PPB_ECP 0x6 /* ECP mode */
|
||||
#define PPB_UNKNOWN 0x7 /* the last one */
|
||||
|
||||
#define PPB_IS_EPP(mode) (mode == PPB_EPP || mode == PPB_ECP_EPP)
|
||||
|
||||
#define PPB_IN_EPP_MODE(dev) (PPB_IS_EPP (ppb_get_mode (dev)))
|
||||
|
||||
/*
|
||||
* Parallel Port Chipset control bits.
|
||||
*/
|
||||
#define STROBE 0x01
|
||||
#define AUTOFEED 0x02
|
||||
#define nINIT 0x04
|
||||
#define SELECTIN 0x08
|
||||
#define PCD 0x20
|
||||
|
||||
/*
|
||||
* Parallel Port Chipset status bits.
|
||||
*/
|
||||
#define TIMEOUT 0x01
|
||||
#define nFAULT 0x08
|
||||
#define SELECT 0x10
|
||||
#define ERROR 0x20
|
||||
#define nACK 0x40
|
||||
#define nBUSY 0x80
|
||||
|
||||
/*
|
||||
* Structure to store status information.
|
||||
*/
|
||||
struct ppb_status {
|
||||
unsigned char status;
|
||||
|
||||
unsigned int timeout:1;
|
||||
unsigned int error:1;
|
||||
unsigned int select:1;
|
||||
unsigned int paper_end:1;
|
||||
unsigned int ack:1;
|
||||
unsigned int busy:1;
|
||||
};
|
||||
|
||||
/*
|
||||
* How tsleep () is called in ppb_request_bus ().
|
||||
*/
|
||||
#define PPB_DONTWAIT 0
|
||||
#define PPB_NOINTR 0
|
||||
#define PPB_WAIT 0x1
|
||||
#define PPB_INTR 0x2
|
||||
|
||||
struct ppb_data; /* see below */
|
||||
|
||||
/*
|
||||
* Parallel Port Bus Device structure.
|
||||
*/
|
||||
struct ppb_device {
|
||||
|
||||
int id_unit; /* unit of the device */
|
||||
|
||||
void (*intr)(int); /* interrupt handler */
|
||||
|
||||
struct ppb_data *ppb; /* link to the ppbus */
|
||||
|
||||
LIST_ENTRY(ppb_device) chain; /* list of devices on the bus */
|
||||
};
|
||||
|
||||
/*
|
||||
* Parallel Port Bus Adapter structure.
|
||||
*/
|
||||
struct ppb_adapter {
|
||||
|
||||
void (*intr_handler)(int);
|
||||
void (*reset_epp_timeout)(int);
|
||||
void (*ecp_sync)(int);
|
||||
|
||||
void (*outsb_epp)(int, char *, int);
|
||||
void (*outsw_epp)(int, char *, int);
|
||||
void (*outsl_epp)(int, char *, int);
|
||||
void (*insb_epp)(int, char *, int);
|
||||
void (*insw_epp)(int, char *, int);
|
||||
void (*insl_epp)(int, char *, int);
|
||||
|
||||
char (*r_dtr)(int);
|
||||
char (*r_str)(int);
|
||||
char (*r_ctr)(int);
|
||||
char (*r_epp)(int);
|
||||
char (*r_ecr)(int);
|
||||
char (*r_fifo)(int);
|
||||
|
||||
void (*w_dtr)(int, char);
|
||||
void (*w_str)(int, char);
|
||||
void (*w_ctr)(int, char);
|
||||
void (*w_epp)(int, char);
|
||||
void (*w_ecr)(int, char);
|
||||
void (*w_fifo)(int, char);
|
||||
};
|
||||
|
||||
/*
|
||||
* ppb_link structure.
|
||||
*/
|
||||
struct ppb_link {
|
||||
|
||||
int adapter_unit; /* unit of the adapter */
|
||||
|
||||
int id_irq; /* != 0 if irq enabled */
|
||||
int mode; /* NIBBLE, PS2, EPP, ECP */
|
||||
|
||||
#define EPP_1_9 0x0 /* default */
|
||||
#define EPP_1_7 0x1
|
||||
|
||||
int epp_protocol; /* EPP protocol: 0=1.9, 1=1.7 */
|
||||
|
||||
struct ppb_adapter *adapter; /* link to the ppc adapter */
|
||||
struct ppb_data *ppbus; /* link to the ppbus */
|
||||
};
|
||||
|
||||
/*
|
||||
* Parallel Port Bus structure.
|
||||
*/
|
||||
struct ppb_data {
|
||||
|
||||
struct ppb_link *ppb_link; /* link to the adapter */
|
||||
struct ppb_device *ppb_owner; /* device which owns the bus */
|
||||
LIST_HEAD(, ppb_device) ppb_devs; /* list of devices on the bus */
|
||||
LIST_ENTRY(ppb_data) ppb_chain; /* list of busses */
|
||||
};
|
||||
|
||||
/*
|
||||
* Parallel Port Bus driver structure.
|
||||
*/
|
||||
struct ppb_driver
|
||||
{
|
||||
struct ppb_device *(*probe)(struct ppb_data *ppb);
|
||||
int (*attach)(struct ppb_device *pdp);
|
||||
char *name;
|
||||
};
|
||||
|
||||
extern struct linker_set ppbdriver_set;
|
||||
|
||||
extern struct ppb_data *ppb_alloc_bus(void);
|
||||
extern int ppb_attachdevs(struct ppb_data *);
|
||||
extern int ppb_request_bus(struct ppb_device *, int);
|
||||
extern int ppb_release_bus(struct ppb_device *);
|
||||
extern void ppb_intr(struct ppb_link *);
|
||||
|
||||
extern int ppb_reset_epp_timeout(struct ppb_device *);
|
||||
extern int ppb_ecp_sync(struct ppb_device *);
|
||||
extern int ppb_get_status(struct ppb_device *, struct ppb_status *);
|
||||
extern int ppb_get_mode(struct ppb_device *);
|
||||
extern int ppb_get_epp_protocol(struct ppb_device *);
|
||||
extern int ppb_get_irq(struct ppb_device *);
|
||||
|
||||
/*
|
||||
* These are defined as macros for speedup.
|
||||
*/
|
||||
#define ppb_outsb_epp(dev,buf,cnt) \
|
||||
(*(dev)->ppb->ppb_link->adapter->outsb_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
|
||||
#define ppb_outsw_epp(dev,buf,cnt) \
|
||||
(*(dev)->ppb->ppb_link->adapter->outsw_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
|
||||
#define ppb_outsl_epp(dev,buf,cnt) \
|
||||
(*(dev)->ppb->ppb_link->adapter->outsl_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
|
||||
#define ppb_insb_epp(dev,buf,cnt) \
|
||||
(*(dev)->ppb->ppb_link->adapter->insb_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
|
||||
#define ppb_insw_epp(dev,buf,cnt) \
|
||||
(*(dev)->ppb->ppb_link->adapter->insw_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
|
||||
#define ppb_insl_epp(dev,buf,cnt) \
|
||||
(*(dev)->ppb->ppb_link->adapter->insl_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
|
||||
|
||||
#define ppb_rdtr(dev) (*(dev)->ppb->ppb_link->adapter->r_dtr) \
|
||||
((dev)->ppb->ppb_link->adapter_unit)
|
||||
#define ppb_rstr(dev) (*(dev)->ppb->ppb_link->adapter->r_str) \
|
||||
((dev)->ppb->ppb_link->adapter_unit)
|
||||
#define ppb_rctr(dev) (*(dev)->ppb->ppb_link->adapter->r_ctr) \
|
||||
((dev)->ppb->ppb_link->adapter_unit)
|
||||
#define ppb_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit)
|
||||
#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \
|
||||
((dev)->ppb->ppb_link->adapter_unit)
|
||||
#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \
|
||||
((dev)->ppb->ppb_link->adapter_unit)
|
||||
|
||||
#define ppb_wdtr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_dtr) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, byte)
|
||||
#define ppb_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, byte)
|
||||
#define ppb_wctr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ctr) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, byte)
|
||||
#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, byte)
|
||||
#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, byte)
|
||||
#define ppb_wfifo(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_fifo) \
|
||||
((dev)->ppb->ppb_link->adapter_unit, byte)
|
||||
|
||||
#endif
|
172
sys/dev/ppbus/ppi.c
Normal file
172
sys/dev/ppbus/ppi.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
|
||||
*
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/malloc.h>
|
||||
|
||||
#include <machine/stdarg.h>
|
||||
#include <machine/clock.h>
|
||||
|
||||
#include <i386/isa/isa.h>
|
||||
#include <i386/isa/isa_device.h>
|
||||
|
||||
#include <sys/kernel.h>
|
||||
#endif /*KERNEL */
|
||||
|
||||
#include <dev/ppbus/ppbconf.h>
|
||||
#include <dev/ppbus/ppi.h>
|
||||
|
||||
static int nppi = 0;
|
||||
#define MAXPPI 8 /* XXX not much better! */
|
||||
static struct ppi_data *ppidata[MAXPPI];
|
||||
|
||||
/*
|
||||
* Make ourselves visible as a ppbus driver
|
||||
*/
|
||||
|
||||
static struct ppb_device *ppiprobe(struct ppb_data *ppb);
|
||||
static int ppiattach(struct ppb_device *dev);
|
||||
static void ppiintr(int unit);
|
||||
|
||||
#ifdef KERNEL
|
||||
|
||||
static struct ppb_driver ppidriver = {
|
||||
ppiprobe, ppiattach, "ppi"
|
||||
};
|
||||
DATA_SET(ppbdriver_set, ppidriver);
|
||||
|
||||
#endif /* KERNEL */
|
||||
|
||||
static d_open_t ppiopen;
|
||||
static d_close_t ppiclose;
|
||||
static d_ioctl_t ppiioctl;
|
||||
|
||||
#define CDEV_MAJOR 14 /* XXX */
|
||||
static struct cdevsw ppi_cdevsw =
|
||||
{ ppiopen, ppiclose, noread, nowrite,
|
||||
ppiioctl, nullstop, nullreset, nodevtotty,
|
||||
seltrue, nommap, nostrat, "ppi", NULL, -1 };
|
||||
|
||||
/*
|
||||
* ppiprobe()
|
||||
*/
|
||||
static struct ppb_device *
|
||||
ppiprobe(struct ppb_data *ppb)
|
||||
{
|
||||
struct ppi_data *ppi;
|
||||
|
||||
ppi = (struct ppi_data *) malloc(sizeof(struct ppi_data),
|
||||
M_TEMP, M_NOWAIT);
|
||||
if (!ppi) {
|
||||
printf("ppi: cannot malloc!\n");
|
||||
return 0;
|
||||
}
|
||||
bzero(ppi, sizeof(struct ppi_data));
|
||||
|
||||
ppidata[nppi] = ppi;
|
||||
|
||||
/*
|
||||
* ppi dependent initialisation.
|
||||
*/
|
||||
ppi->ppi_unit = nppi;
|
||||
|
||||
/*
|
||||
* ppbus dependent initialisation.
|
||||
*/
|
||||
ppi->ppi_dev.id_unit = ppi->ppi_unit;
|
||||
ppi->ppi_dev.ppb = ppb;
|
||||
ppi->ppi_dev.intr = ppiintr;
|
||||
|
||||
/* Ok, go to next device on next probe */
|
||||
nppi ++;
|
||||
|
||||
return &ppi->ppi_dev;
|
||||
}
|
||||
|
||||
static int
|
||||
ppiattach(struct ppb_device *dev)
|
||||
{
|
||||
struct ppi_data *ppi = ppidata[dev->id_unit];
|
||||
|
||||
/*
|
||||
* Report ourselves
|
||||
*/
|
||||
printf("ppi%d: <generic parallel i/o> on ppbus %d\n",
|
||||
dev->id_unit, dev->ppb->ppb_link->adapter_unit);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
ppiintr(int unit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
ppiopen(dev_t dev, int flags, int fmt, struct proc *p)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
ppiclose(dev_t dev, int flags, int fmt, struct proc *p)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
ppiioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static ppi_devsw_installed = 0;
|
||||
|
||||
static void ppi_drvinit(void *unused)
|
||||
{
|
||||
dev_t dev;
|
||||
|
||||
if( ! ppi_devsw_installed ) {
|
||||
dev = makedev(CDEV_MAJOR, 0);
|
||||
cdevsw_add(&dev, &ppi_cdevsw, NULL);
|
||||
ppi_devsw_installed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
SYSINIT(ppidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, ppi_drvinit, NULL)
|
39
sys/dev/ppbus/ppi.h
Normal file
39
sys/dev/ppbus/ppi.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
|
||||
*
|
||||
*/
|
||||
#ifndef __PPI_H
|
||||
#define __PPI_H
|
||||
|
||||
struct ppi_data {
|
||||
|
||||
int ppi_unit;
|
||||
|
||||
struct ppb_device ppi_dev;
|
||||
};
|
||||
|
||||
#endif
|
860
sys/dev/ppbus/vpo.c
Normal file
860
sys/dev/ppbus/vpo.c
Normal file
@ -0,0 +1,860 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
|
||||
*
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#include <machine/stdarg.h>
|
||||
#include <machine/clock.h>
|
||||
|
||||
#include <i386/isa/isa_device.h>
|
||||
#endif /* KERNEL */
|
||||
#include <scsi/scsi_all.h>
|
||||
#include <scsi/scsi_disk.h>
|
||||
#include <scsi/scsiconf.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
#include <sys/kernel.h>
|
||||
#endif /*KERNEL */
|
||||
|
||||
#include <dev/ppbus/ppbconf.h>
|
||||
#include <dev/ppbus/vpo.h>
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* HERE ARE THINGS YOU MAY HAVE/WANT TO CHANGE
|
||||
*/
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* We may add a timeout queue to avoid active polling on nACK.
|
||||
*/
|
||||
#define VP0_SELTMO 5000 /* select timeout */
|
||||
#define VP0_FAST_SPINTMO 500000 /* wait status timeout */
|
||||
#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */
|
||||
|
||||
/* XXX
|
||||
* This is ALPHA/BETA code, warnings are mandatory.
|
||||
*/
|
||||
#ifndef VP0_WARNING
|
||||
#define VP0_WARNING /* defined to get warnings about timeouts,
|
||||
* except select timeouts */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DO NOT MODIFY ANYTHING UNDER THIS LINE
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static inline int vpoio_do_scsi(struct vpo_data *, int, int, char *, int,
|
||||
char *, int, int *, int *);
|
||||
|
||||
static int32_t vpo_scsi_cmd(struct scsi_xfer *);
|
||||
static void vpominphys(struct buf *);
|
||||
static u_int32_t vpo_adapter_info(int);
|
||||
|
||||
static int vpo_detect(struct vpo_data *vpo);
|
||||
|
||||
static int nvpo = 0;
|
||||
#define MAXVP0 8 /* XXX not much better! */
|
||||
static struct vpo_data *vpodata[MAXVP0];
|
||||
|
||||
#ifdef KERNEL
|
||||
static struct scsi_adapter vpo_switch =
|
||||
{
|
||||
vpo_scsi_cmd,
|
||||
vpominphys,
|
||||
0,
|
||||
0,
|
||||
vpo_adapter_info,
|
||||
"vpo",
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* The below structure is so we have a default dev struct
|
||||
* for out link struct.
|
||||
*/
|
||||
static struct scsi_device vpo_dev =
|
||||
{
|
||||
NULL, /* Use default error handler */
|
||||
NULL, /* have a queue, served by this */
|
||||
NULL, /* have no async handler */
|
||||
NULL, /* Use default 'done' routine */
|
||||
"vpo",
|
||||
0,
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Make ourselves visible as a ppbus driver
|
||||
*/
|
||||
|
||||
static struct ppb_device *vpoprobe(struct ppb_data *ppb);
|
||||
static int vpoattach(struct ppb_device *dev);
|
||||
|
||||
static struct ppb_driver vpodriver = {
|
||||
vpoprobe, vpoattach, "vpo"
|
||||
};
|
||||
DATA_SET(ppbdriver_set, vpodriver);
|
||||
|
||||
|
||||
#endif /* KERNEL */
|
||||
|
||||
static u_int32_t
|
||||
vpo_adapter_info(int unit)
|
||||
{
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoprobe()
|
||||
*
|
||||
* Called by ppb_attachdevs().
|
||||
*/
|
||||
static struct ppb_device *
|
||||
vpoprobe(struct ppb_data *ppb)
|
||||
{
|
||||
|
||||
struct vpo_data *vpo;
|
||||
|
||||
if (nvpo >= MAXVP0) {
|
||||
printf("vpo: Too many devices (max %d)\n", MAXVP0);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
vpo = (struct vpo_data *)malloc(sizeof(struct vpo_data),
|
||||
M_DEVBUF, M_NOWAIT);
|
||||
if (!vpo) {
|
||||
printf("vpo: cannot malloc!\n");
|
||||
return(NULL);
|
||||
}
|
||||
bzero(vpo, sizeof(struct vpo_data));
|
||||
|
||||
vpodata[nvpo] = vpo;
|
||||
|
||||
/* vpo dependent initialisation */
|
||||
vpo->vpo_unit = nvpo;
|
||||
|
||||
/* ppbus dependent initialisation */
|
||||
vpo->vpo_dev.id_unit = vpo->vpo_unit;
|
||||
vpo->vpo_dev.ppb = ppb;
|
||||
|
||||
/* now, try to initialise the drive */
|
||||
if (vpo_detect(vpo) != 0) {
|
||||
free(vpo, M_DEVBUF);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/* ok, go to next device on next probe */
|
||||
nvpo ++;
|
||||
|
||||
return (&vpo->vpo_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoattach()
|
||||
*
|
||||
* Called by ppb_attachdevs().
|
||||
*/
|
||||
static int
|
||||
vpoattach(struct ppb_device *dev)
|
||||
{
|
||||
|
||||
struct scsibus_data *scbus;
|
||||
struct vpo_data *vpo = vpodata[dev->id_unit];
|
||||
|
||||
vpo->sc_link.adapter_unit = vpo->vpo_unit;
|
||||
vpo->sc_link.adapter_targ = VP0_INITIATOR;
|
||||
vpo->sc_link.adapter = &vpo_switch;
|
||||
vpo->sc_link.device = &vpo_dev;
|
||||
vpo->sc_link.opennings = VP0_OPENNINGS;
|
||||
|
||||
/*
|
||||
* Report ourselves
|
||||
*/
|
||||
printf("vpo%d: <Adaptec aic7110 scsi> on ppbus %d\n",
|
||||
dev->id_unit, dev->ppb->ppb_link->adapter_unit);
|
||||
|
||||
/*
|
||||
* Prepare the scsibus_data area for the upperlevel
|
||||
* scsi code.
|
||||
*/
|
||||
scbus = scsi_alloc_bus();
|
||||
if(!scbus)
|
||||
return (0);
|
||||
scbus->adapter_link = &vpo->sc_link;
|
||||
|
||||
scsi_attachdevs(scbus);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
vpominphys(struct buf *bp)
|
||||
{
|
||||
|
||||
if (bp->b_bcount > VP0_BUFFER_SIZE)
|
||||
bp->b_bcount = VP0_BUFFER_SIZE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef VP0_WARNING
|
||||
static inline void
|
||||
vpo_warning(struct vpo_data *vpo, struct scsi_xfer *xs, int timeout)
|
||||
{
|
||||
|
||||
switch (timeout) {
|
||||
case 0:
|
||||
case VP0_ESELECT_TIMEOUT:
|
||||
/* log(LOG_WARNING,
|
||||
"vpo%d: select timeout\n", vpo->vpo_unit); */
|
||||
break;
|
||||
case VP0_EDISCONNECT:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: can't get printer state\n", vpo->vpo_unit);
|
||||
break;
|
||||
case VP0_ECONNECT:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: can't get disk state\n", vpo->vpo_unit);
|
||||
break;
|
||||
case VP0_ECMD_TIMEOUT:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: command timeout\n", vpo->vpo_unit);
|
||||
break;
|
||||
case VP0_EPPDATA_TIMEOUT:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: EPP data timeout\n", vpo->vpo_unit);
|
||||
break;
|
||||
case VP0_ESTATUS_TIMEOUT:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: status timeout\n", vpo->vpo_unit);
|
||||
break;
|
||||
case VP0_EDATA_OVERFLOW:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: data overflow\n", vpo->vpo_unit);
|
||||
break;
|
||||
case VP0_EINTR:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: ppb request interrupted\n", vpo->vpo_unit);
|
||||
break;
|
||||
default:
|
||||
log(LOG_WARNING,
|
||||
"vpo%d: timeout = %d\n", vpo->vpo_unit, timeout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* VP0_WARNING */
|
||||
|
||||
/*
|
||||
* vpointr()
|
||||
*/
|
||||
static inline void
|
||||
vpointr(struct vpo_data *vpo, struct scsi_xfer *xs)
|
||||
{
|
||||
|
||||
register int timeout;
|
||||
|
||||
if (xs->datalen && !(xs->flags & SCSI_DATA_IN))
|
||||
bcopy(xs->data, vpo->vpo_buffer, xs->datalen);
|
||||
|
||||
timeout = vpoio_do_scsi(vpo, VP0_INITIATOR,
|
||||
xs->sc_link->target,
|
||||
(char *)xs->cmd, xs->cmdlen,
|
||||
vpo->vpo_buffer, xs->datalen,
|
||||
&vpo->vpo_stat, &vpo->vpo_count);
|
||||
|
||||
#ifdef VP0_WARNING
|
||||
vpo_warning(vpo, xs, timeout);
|
||||
#endif
|
||||
|
||||
#ifdef VP03_DEBUG
|
||||
printf("vpo_do_scsi = %d, status = 0x%x, count = %d\n",
|
||||
timeout, vpo->vpo_stat, vpo->vpo_count);
|
||||
#endif
|
||||
|
||||
#define RESERVED_BITS_MASK 0x3e /* 00111110b */
|
||||
#define NO_SENSE 0x0
|
||||
#define CHECK_CONDITION 0x02
|
||||
|
||||
switch (vpo->vpo_stat & RESERVED_BITS_MASK) {
|
||||
case NO_SENSE:
|
||||
break;
|
||||
|
||||
case CHECK_CONDITION:
|
||||
default:
|
||||
vpo->vpo_sense.cmd.op_code = REQUEST_SENSE;
|
||||
vpo->vpo_sense.cmd.length = sizeof(xs->sense);
|
||||
vpo->vpo_sense.cmd.control = 0;
|
||||
|
||||
timeout = vpoio_do_scsi(vpo, VP0_INITIATOR,
|
||||
xs->sc_link->target,
|
||||
(char *)&vpo->vpo_sense.cmd,
|
||||
sizeof(vpo->vpo_sense.cmd),
|
||||
(char *)&xs->sense, sizeof(xs->sense),
|
||||
&vpo->vpo_sense.stat, &vpo->vpo_sense.count);
|
||||
|
||||
xs->error = XS_SENSE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
xs->error = XS_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (xs->datalen && (xs->flags & SCSI_DATA_IN))
|
||||
bcopy(vpo->vpo_buffer, xs->data, xs->datalen);
|
||||
|
||||
done:
|
||||
xs->resid = 0;
|
||||
xs->error = XS_NOERROR;
|
||||
|
||||
error:
|
||||
xs->flags |= ITSDONE;
|
||||
scsi_done(xs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
vpo_scsi_cmd(struct scsi_xfer *xs)
|
||||
{
|
||||
|
||||
int s;
|
||||
|
||||
if (xs->sc_link->lun > 0) {
|
||||
xs->error = XS_DRIVER_STUFFUP;
|
||||
return TRY_AGAIN_LATER;
|
||||
}
|
||||
|
||||
if (xs->flags & SCSI_DATA_UIO) {
|
||||
printf("UIO not supported by vpo_driver !\n");
|
||||
xs->error = XS_DRIVER_STUFFUP;
|
||||
return TRY_AGAIN_LATER;
|
||||
}
|
||||
|
||||
#ifdef VP03_DEBUG
|
||||
printf("vpo_scsi_cmd(): xs->flags = 0x%x, "\
|
||||
"xs->data = 0x%x, xs->datalen = %d\ncommand : %*D\n",
|
||||
xs->flags, xs->data, xs->datalen,
|
||||
xs->cmdlen, xs->cmd, " " );
|
||||
#endif
|
||||
|
||||
if (xs->flags & SCSI_NOMASK) {
|
||||
vpointr(vpodata[xs->sc_link->adapter_unit], xs);
|
||||
return COMPLETE;
|
||||
}
|
||||
|
||||
s = VP0_SPL();
|
||||
|
||||
vpointr(vpodata[xs->sc_link->adapter_unit], xs);
|
||||
|
||||
splx(s);
|
||||
return SUCCESSFULLY_QUEUED;
|
||||
}
|
||||
|
||||
#define vpoio_d_pulse(vpo,b) { \
|
||||
ppb_wdtr(&(vpo)->vpo_dev, b); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
}
|
||||
|
||||
#define vpoio_c_pulse(vpo,b) { \
|
||||
ppb_wdtr(&(vpo)->vpo_dev, b); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
}
|
||||
|
||||
static int
|
||||
vpoio_disconnect(struct vpo_data *vpo)
|
||||
{
|
||||
|
||||
vpoio_d_pulse(vpo, 0);
|
||||
vpoio_d_pulse(vpo, 0x3c);
|
||||
vpoio_d_pulse(vpo, 0x20);
|
||||
vpoio_d_pulse(vpo, 0xf);
|
||||
|
||||
return (ppb_release_bus(&vpo->vpo_dev));
|
||||
}
|
||||
|
||||
/*
|
||||
* how : PPB_WAIT or PPB_DONTWAIT
|
||||
*/
|
||||
static int
|
||||
vpoio_connect(struct vpo_data *vpo, int how)
|
||||
{
|
||||
int error;
|
||||
|
||||
if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
|
||||
return error;
|
||||
|
||||
vpoio_c_pulse(vpo, 0);
|
||||
vpoio_c_pulse(vpo, 0x3c);
|
||||
vpoio_c_pulse(vpo, 0x20);
|
||||
|
||||
if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) {
|
||||
vpoio_c_pulse(vpo, 0xcf);
|
||||
} else {
|
||||
vpoio_c_pulse(vpo, 0x8f);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoio_in_disk_mode()
|
||||
*
|
||||
* Check if we are in disk mode
|
||||
*/
|
||||
static int
|
||||
vpoio_in_disk_mode(struct vpo_data *vpo)
|
||||
{
|
||||
|
||||
/* first, set H_AUTO high */
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
|
||||
/* when H_AUTO is set low, H_FLT should be high */
|
||||
ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) == 0)
|
||||
return (0);
|
||||
|
||||
/* when H_AUTO is set high, H_FLT should be low */
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) != 0)
|
||||
return (0);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoio_reset()
|
||||
*
|
||||
* SCSI reset signal, the drive must be in disk mode
|
||||
*/
|
||||
static void
|
||||
vpoio_reset (struct vpo_data *vpo)
|
||||
{
|
||||
|
||||
/*
|
||||
* SCSI reset signal.
|
||||
*/
|
||||
ppb_wdtr(&vpo->vpo_dev, (1 << 7));
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
|
||||
DELAY(25);
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* vpo_detect()
|
||||
*
|
||||
* Detect and initialise the VP0 adapter.
|
||||
*/
|
||||
static int
|
||||
vpo_detect(struct vpo_data *vpo)
|
||||
{
|
||||
|
||||
vpoio_disconnect(vpo);
|
||||
vpoio_connect(vpo, PPB_DONTWAIT);
|
||||
|
||||
if (!vpoio_in_disk_mode(vpo)) {
|
||||
vpoio_disconnect(vpo);
|
||||
return (VP0_EINITFAILED);
|
||||
}
|
||||
|
||||
/* send SCSI reset signal */
|
||||
vpoio_reset (vpo);
|
||||
|
||||
vpoio_disconnect(vpo);
|
||||
|
||||
if (vpoio_in_disk_mode(vpo))
|
||||
return (VP0_EINITFAILED);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#define vpo_wctr(dev,byte,delay) { \
|
||||
int i; int iter = delay / MHZ_16_IO_DURATION; \
|
||||
for (i = 0; i < iter; i++) { \
|
||||
ppb_wctr(dev, byte); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define vpoio_spp_outbyte(vpo,byte) { \
|
||||
ppb_wdtr(&vpo->vpo_dev, byte); \
|
||||
ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
|
||||
vpo_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE, \
|
||||
VP0_SPP_WRITE_PULSE); \
|
||||
}
|
||||
|
||||
#define vpoio_nibble_inbyte(vpo,buffer) { \
|
||||
register char h, l; \
|
||||
vpo_wctr(&vpo->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE, \
|
||||
VP0_NIBBLE_READ_PULSE); \
|
||||
h = ppb_rstr(&vpo->vpo_dev); \
|
||||
ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
|
||||
l = ppb_rstr(&vpo->vpo_dev); \
|
||||
*buffer = ((l >> 4) & 0x0f) + (h & 0xf0); \
|
||||
}
|
||||
|
||||
#define vpoio_ps2_inbyte(vpo,buffer) { \
|
||||
*buffer = ppb_rdtr(&vpo->vpo_dev); \
|
||||
ppb_wctr(&vpo->vpo_dev, PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE); \
|
||||
ppb_wctr(&vpo->vpo_dev, PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE); \
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoio_outstr()
|
||||
*/
|
||||
static int
|
||||
vpoio_outstr(struct vpo_data *vpo, char *buffer, int size)
|
||||
{
|
||||
|
||||
register int k;
|
||||
int error = 0;
|
||||
int r, mode, epp;
|
||||
|
||||
mode = ppb_get_mode(&vpo->vpo_dev);
|
||||
switch (mode) {
|
||||
case PPB_NIBBLE:
|
||||
case PPB_PS2:
|
||||
for (k = 0; k < size; k++) {
|
||||
vpoio_spp_outbyte(vpo, *buffer++);
|
||||
}
|
||||
break;
|
||||
|
||||
case PPB_EPP:
|
||||
case PPB_ECP_EPP:
|
||||
epp = ppb_get_epp_protocol(&vpo->vpo_dev);
|
||||
|
||||
ppb_reset_epp_timeout(&vpo->vpo_dev);
|
||||
ppb_wctr(&vpo->vpo_dev,
|
||||
H_AUTO | H_SELIN | H_INIT | H_STROBE);
|
||||
|
||||
if (epp == EPP_1_7)
|
||||
for (k = 0; k < size; k++) {
|
||||
ppb_wepp(&vpo->vpo_dev, *buffer++);
|
||||
if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
|
||||
error = VP0_EPPDATA_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (((long) buffer | size) & 0x03)
|
||||
ppb_outsb_epp(&vpo->vpo_dev,
|
||||
buffer, size);
|
||||
else
|
||||
ppb_outsl_epp(&vpo->vpo_dev,
|
||||
buffer, size/4);
|
||||
|
||||
if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
|
||||
error = VP0_EPPDATA_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ppb_wctr(&vpo->vpo_dev,
|
||||
H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
/* ppb_ecp_sync(&vpo->vpo_dev); */
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("vpoio_outstr(): unknown transfer mode (%d)!\n",
|
||||
mode);
|
||||
return (1); /* XXX */
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoio_instr()
|
||||
*/
|
||||
static int
|
||||
vpoio_instr(struct vpo_data *vpo, char *buffer, int size)
|
||||
{
|
||||
|
||||
register int k;
|
||||
int error = 0;
|
||||
int r, mode, epp;
|
||||
|
||||
mode = ppb_get_mode(&vpo->vpo_dev);
|
||||
switch (mode) {
|
||||
case PPB_NIBBLE:
|
||||
for (k = 0; k < size; k++) {
|
||||
vpoio_nibble_inbyte(vpo, buffer++);
|
||||
}
|
||||
ppb_wctr(&vpo->vpo_dev,
|
||||
H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
break;
|
||||
|
||||
case PPB_PS2:
|
||||
ppb_wctr(&vpo->vpo_dev, PCD |
|
||||
H_AUTO | H_SELIN | H_INIT | H_nSTROBE);
|
||||
|
||||
for (k = 0; k < size; k++) {
|
||||
vpoio_ps2_inbyte(vpo, buffer++);
|
||||
}
|
||||
ppb_wctr(&vpo->vpo_dev,
|
||||
H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
break;
|
||||
|
||||
case PPB_EPP:
|
||||
case PPB_ECP_EPP:
|
||||
epp = ppb_get_epp_protocol(&vpo->vpo_dev);
|
||||
|
||||
ppb_reset_epp_timeout(&vpo->vpo_dev);
|
||||
ppb_wctr(&vpo->vpo_dev, PCD |
|
||||
H_AUTO | H_SELIN | H_INIT | H_STROBE);
|
||||
|
||||
if (epp == EPP_1_7)
|
||||
for (k = 0; k < size; k++) {
|
||||
*buffer++ = ppb_repp(&vpo->vpo_dev);
|
||||
if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
|
||||
error = VP0_EPPDATA_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (((long) buffer | size) & 0x03)
|
||||
ppb_insb_epp(&vpo->vpo_dev,
|
||||
buffer, size);
|
||||
else
|
||||
ppb_insl_epp(&vpo->vpo_dev,
|
||||
buffer, size/4);
|
||||
|
||||
if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
|
||||
error = VP0_EPPDATA_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ppb_wctr(&vpo->vpo_dev, PCD |
|
||||
H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
/* ppb_ecp_sync(&vpo->vpo_dev); */
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("vpoio_instr(): unknown transfer mode (%d)!\n",
|
||||
mode);
|
||||
return (1); /* XXX */
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static inline char
|
||||
vpoio_select(struct vpo_data *vpo, int initiator, int target)
|
||||
{
|
||||
|
||||
register int k;
|
||||
|
||||
ppb_wdtr(&vpo->vpo_dev, (1 << target));
|
||||
ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
ppb_wdtr(&vpo->vpo_dev, (1 << initiator));
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
|
||||
|
||||
k = 0;
|
||||
while (!(ppb_rstr(&vpo->vpo_dev) & 0x40) && (k++ < VP0_SELTMO))
|
||||
barrier();
|
||||
|
||||
if (k >= VP0_SELTMO)
|
||||
return (VP0_ESELECT_TIMEOUT);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* vpoio_wait()
|
||||
*
|
||||
* H_SELIN must be low.
|
||||
*/
|
||||
static inline char
|
||||
vpoio_wait(struct vpo_data *vpo, int tmo)
|
||||
{
|
||||
|
||||
register int k;
|
||||
register char r;
|
||||
|
||||
k = 0;
|
||||
while (!((r = ppb_rstr(&vpo->vpo_dev)) & 0x80) && (k++ < tmo))
|
||||
barrier();
|
||||
|
||||
/*
|
||||
* Return some status information.
|
||||
* Semantics : 0xc0 = ZIP wants more data
|
||||
* 0xd0 = ZIP wants to send more data
|
||||
* 0xe0 = ZIP wants command
|
||||
* 0xf0 = end of transfer, ZIP is sending status
|
||||
*/
|
||||
if (k < tmo)
|
||||
return (r & 0xf0);
|
||||
|
||||
return (0); /* command timed out */
|
||||
}
|
||||
|
||||
static inline int
|
||||
vpoio_do_scsi(struct vpo_data *vpo, int host, int target, char *command,
|
||||
int clen, char *buffer, int blen, int *result, int *count)
|
||||
{
|
||||
|
||||
register char r;
|
||||
char l, h = 0;
|
||||
int rw, len, error = 0;
|
||||
register int k;
|
||||
|
||||
/* enter disk state, allocate the ppbus */
|
||||
vpoio_connect(vpo, PPB_WAIT | PPB_NOINTR);
|
||||
|
||||
if (!vpoio_in_disk_mode(vpo)) {
|
||||
error = VP0_ECONNECT; goto error;
|
||||
}
|
||||
|
||||
if ((error = vpoio_select(vpo,host,target)))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Send the command ...
|
||||
*
|
||||
* set H_SELIN low for vpoio_wait().
|
||||
*/
|
||||
ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
|
||||
|
||||
#ifdef VP03_DEBUG
|
||||
printf("vpo%d: drive selected, now sending the command...\n",
|
||||
vpo->vpo_unit);
|
||||
#endif
|
||||
|
||||
for (k = 0; k < clen; k++) {
|
||||
if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
|
||||
error = VP0_ECMD_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
if (vpoio_outstr(vpo, &command[k], 1)) {
|
||||
error = VP0_EPPDATA_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VP03_DEBUG
|
||||
printf("vpo%d: command sent, now completing the request...\n",
|
||||
vpo->vpo_unit);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Completion ...
|
||||
*/
|
||||
rw = ((command[0] == READ_COMMAND) || (command[0] == READ_BIG) ||
|
||||
(command[0] == WRITE_COMMAND) || (command[0] == WRITE_BIG));
|
||||
|
||||
*count = 0;
|
||||
for (;;) {
|
||||
|
||||
if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
|
||||
error = VP0_ESTATUS_TIMEOUT; goto error;
|
||||
}
|
||||
|
||||
/* stop when the ZIP wants to send status */
|
||||
if (r == (char)0xf0)
|
||||
break;
|
||||
|
||||
if (*count >= blen) {
|
||||
error = VP0_EDATA_OVERFLOW;
|
||||
goto error;
|
||||
}
|
||||
len = (rw && ((blen - *count) >= VP0_SECTOR_SIZE)) ?
|
||||
VP0_SECTOR_SIZE : 1;
|
||||
|
||||
/* ZIP wants to send data? */
|
||||
if (r == (char)0xc0)
|
||||
error = vpoio_outstr(vpo, &buffer[*count], len);
|
||||
else
|
||||
error = vpoio_instr(vpo, &buffer[*count], len);
|
||||
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
*count += len;
|
||||
}
|
||||
|
||||
if (vpoio_instr(vpo, &l, 1)) {
|
||||
error = VP0_EOTHER; goto error;
|
||||
}
|
||||
|
||||
/* check if the ZIP wants to send more status */
|
||||
if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
|
||||
if (vpoio_instr(vpo, &h, 1)) {
|
||||
error = VP0_EOTHER+2; goto error;
|
||||
}
|
||||
|
||||
/* return to printer state */
|
||||
vpoio_disconnect(vpo);
|
||||
|
||||
#if 0
|
||||
if (vpoio_in_disk_mode(vpo)) {
|
||||
vpoio_reset (vpo);
|
||||
error = VP0_EDISCONNECT; goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
*result = ((int) h << 8) | ((int) l & 0xff);
|
||||
|
||||
return (0);
|
||||
|
||||
error:
|
||||
vpoio_disconnect(vpo);
|
||||
return (error);
|
||||
}
|
109
sys/dev/ppbus/vpo.h
Normal file
109
sys/dev/ppbus/vpo.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*-
|
||||
* Copyright (c) 1997 Nicolas Souchu
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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$
|
||||
*
|
||||
*/
|
||||
#ifndef __VP03_H
|
||||
#define __VP03_H
|
||||
|
||||
#define barrier() __asm__("": : :"memory")
|
||||
|
||||
#define VP0_INITIATOR 0x7
|
||||
|
||||
#define VP0_SECTOR_SIZE 512
|
||||
#define VP0_BUFFER_SIZE 0x12000
|
||||
|
||||
#define VP0_SPL() splbio()
|
||||
|
||||
#define VP0_ESELECT_TIMEOUT 1
|
||||
#define VP0_ECMD_TIMEOUT 2
|
||||
#define VP0_ECONNECT 3
|
||||
#define VP0_ESTATUS_TIMEOUT 4
|
||||
#define VP0_EDATA_OVERFLOW 5
|
||||
#define VP0_EDISCONNECT 6
|
||||
#define VP0_EPPDATA_TIMEOUT 7
|
||||
#define VP0_ENOPORT 9
|
||||
#define VP0_EINITFAILED 10
|
||||
#define VP0_EINTR 12
|
||||
|
||||
#define VP0_EOTHER 13
|
||||
|
||||
#define VP0_OPENNINGS 1
|
||||
|
||||
#define n(flags) (~(flags) & (flags))
|
||||
|
||||
/*
|
||||
* VP0 timings.
|
||||
*/
|
||||
#define MHZ_16_IO_DURATION 62
|
||||
|
||||
#define VP0_SPP_WRITE_PULSE 253
|
||||
#define VP0_NIBBLE_READ_PULSE 486
|
||||
|
||||
/*
|
||||
* VP0 connections.
|
||||
*/
|
||||
#define H_AUTO n(AUTOFEED)
|
||||
#define H_nAUTO AUTOFEED
|
||||
#define H_STROBE n(STROBE)
|
||||
#define H_nSTROBE STROBE
|
||||
#define H_BSY n(nBUSY)
|
||||
#define H_nBSY n_BUSY
|
||||
#define H_SEL SELECT
|
||||
#define H_nSEL n(SELECT)
|
||||
#define H_ERR ERROR
|
||||
#define H_nERR n(ERROR)
|
||||
#define H_ACK nACK
|
||||
#define H_nACK n(nACK)
|
||||
#define H_FLT nFAULT
|
||||
#define H_nFLT n(nFAULT)
|
||||
#define H_SELIN n(SELECTIN)
|
||||
#define H_nSELIN SELECTIN
|
||||
#define H_INIT nINIT
|
||||
#define H_nINIT n(nINIT)
|
||||
|
||||
struct vpo_sense {
|
||||
struct scsi_sense cmd;
|
||||
unsigned int stat;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
struct vpo_data {
|
||||
unsigned short vpo_unit;
|
||||
|
||||
int vpo_stat;
|
||||
int vpo_count;
|
||||
|
||||
struct ppb_status vpo_status;
|
||||
struct vpo_sense vpo_sense;
|
||||
|
||||
unsigned char vpo_buffer[VP0_BUFFER_SIZE];
|
||||
|
||||
struct ppb_device vpo_dev;
|
||||
struct scsi_link sc_link;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user