diff options
author | msmith <msmith@FreeBSD.org> | 1997-08-14 13:57:45 +0000 |
---|---|---|
committer | msmith <msmith@FreeBSD.org> | 1997-08-14 13:57:45 +0000 |
commit | 4cab4fbe44c163678dcf49eff0f7c26780af5748 (patch) | |
tree | 0e0fb58db68b41fb5f2f7cf944e37dbcee0948b6 /sys/dev/ppbus | |
parent | 08ef2a797164241d584a77ec4b88ef78325433d7 (diff) | |
download | FreeBSD-src-4cab4fbe44c163678dcf49eff0f7c26780af5748.zip FreeBSD-src-4cab4fbe44c163678dcf49eff0f7c26780af5748.tar.gz |
New directory and drivers for Parallel Port Bus devices.
Submitted by: Nicolas Souchu <Nicolas.Souchu@prism.uvsq.fr>
Diffstat (limited to 'sys/dev/ppbus')
-rw-r--r-- | sys/dev/ppbus/nlpt.c | 765 | ||||
-rw-r--r-- | sys/dev/ppbus/nlpt.h | 74 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbconf.c | 339 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbconf.h | 239 | ||||
-rw-r--r-- | sys/dev/ppbus/ppi.c | 172 | ||||
-rw-r--r-- | sys/dev/ppbus/ppi.h | 39 | ||||
-rw-r--r-- | sys/dev/ppbus/vpo.c | 860 | ||||
-rw-r--r-- | sys/dev/ppbus/vpo.h | 109 |
8 files changed, 2597 insertions, 0 deletions
diff --git a/sys/dev/ppbus/nlpt.c b/sys/dev/ppbus/nlpt.c new file mode 100644 index 0000000..6e35a12 --- /dev/null +++ b/sys/dev/ppbus/nlpt.c @@ -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) diff --git a/sys/dev/ppbus/nlpt.h b/sys/dev/ppbus/nlpt.h new file mode 100644 index 0000000..85f3dce --- /dev/null +++ b/sys/dev/ppbus/nlpt.h @@ -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 diff --git a/sys/dev/ppbus/ppbconf.c b/sys/dev/ppbus/ppbconf.c new file mode 100644 index 0000000..5d2bc45 --- /dev/null +++ b/sys/dev/ppbus/ppbconf.c @@ -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); +} diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h new file mode 100644 index 0000000..b10df44 --- /dev/null +++ b/sys/dev/ppbus/ppbconf.h @@ -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 diff --git a/sys/dev/ppbus/ppi.c b/sys/dev/ppbus/ppi.c new file mode 100644 index 0000000..79df5b5 --- /dev/null +++ b/sys/dev/ppbus/ppi.c @@ -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) diff --git a/sys/dev/ppbus/ppi.h b/sys/dev/ppbus/ppi.h new file mode 100644 index 0000000..d1a2e1d --- /dev/null +++ b/sys/dev/ppbus/ppi.h @@ -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 diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c new file mode 100644 index 0000000..1cd69be --- /dev/null +++ b/sys/dev/ppbus/vpo.c @@ -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); +} diff --git a/sys/dev/ppbus/vpo.h b/sys/dev/ppbus/vpo.h new file mode 100644 index 0000000..582538f --- /dev/null +++ b/sys/dev/ppbus/vpo.h @@ -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 |