New directory and drivers for Parallel Port Bus devices.

Submitted by:	Nicolas Souchu <Nicolas.Souchu@prism.uvsq.fr>
This commit is contained in:
Mike Smith 1997-08-14 13:57:45 +00:00
parent fb35546309
commit ed3815224c
8 changed files with 2597 additions and 0 deletions

765
sys/dev/ppbus/nlpt.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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