diff options
-rw-r--r-- | share/man/man4/ppbus.4 | 50 | ||||
-rw-r--r-- | sys/dev/ppbus/immio.c | 4 | ||||
-rw-r--r-- | sys/dev/ppbus/lpbb.c | 10 | ||||
-rw-r--r-- | sys/dev/ppbus/nlpt.c | 125 | ||||
-rw-r--r-- | sys/dev/ppbus/nlpt.h | 6 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_1284.c | 770 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_1284.h | 65 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_base.c | 49 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_msq.c | 22 | ||||
-rw-r--r-- | sys/dev/ppbus/ppb_msq.h | 4 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbconf.c | 209 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbconf.h | 25 | ||||
-rw-r--r-- | sys/dev/ppbus/ppi.c | 322 | ||||
-rw-r--r-- | sys/dev/ppbus/vpo.c | 4 | ||||
-rw-r--r-- | sys/dev/ppbus/vpoio.c | 19 | ||||
-rw-r--r-- | sys/dev/ppc/ppc.c | 483 | ||||
-rw-r--r-- | sys/dev/ppc/ppcreg.h | 55 | ||||
-rw-r--r-- | sys/i386/isa/ppc.c | 483 | ||||
-rw-r--r-- | sys/i386/isa/ppcreg.h | 55 | ||||
-rw-r--r-- | sys/isa/ppc.c | 483 | ||||
-rw-r--r-- | sys/isa/ppcreg.h | 55 | ||||
-rw-r--r-- | usr.sbin/lptcontrol/lptcontrol.c | 10 |
22 files changed, 2936 insertions, 372 deletions
diff --git a/share/man/man4/ppbus.4 b/share/man/man4/ppbus.4 index d265dbc..74a06bd 100644 --- a/share/man/man4/ppbus.4 +++ b/share/man/man4/ppbus.4 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1998, Nicolas Souchu +.\" Copyright (c) 1998, 1999 Nicolas Souchu .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -38,6 +38,8 @@ Parallel port bus system .Cd "device nlpt0 at ppbus?" .Cd "device plip0 at ppbus?" .Cd "device ppi0 at ppbus?" +.Cd "device pps0 at ppbus?" +.Cd "device lpbb0 at ppbus?" .Sh DESCRIPTION The .Em ppbus @@ -68,6 +70,8 @@ and non-standard software: .It Sy vpo Ta "VPI0 parallel to Adaptec AIC-7110 SCSI controller driver." It uses standard and non-standard parallel port accesses .It Sy ppi Ta "Parallel port interface for general I/O" +.It Sy pps Ta "Pulse per second Timing Interface" +.It Sy lpbb Ta "Philips official parallel port I2C bit-banging interface" .El .Ss Porting existing drivers .Pp @@ -155,6 +159,50 @@ standard parallel port. Some manufacturers, like SMC, have implemented chipsets that support mixed modes. With such chipsets, mode switching is available at any time by accessing the extended control register. +.Sh IEEE1284-1994 Standard +.Ss Background +This standard is also named "IEEE Standard Signaling Method for a +Bidirectional Parallel Peripheral Interface for Personal Computers". It +defines a signaling method for asynchroneous, fully interlocked, bidirectional +parallel communications between hosts and printers or other peripherals. It +also specifies a format for a peripheral identification string and a method of +returning this string to the host outside of the bidirectional data stream. +.Pp +This standard is architecture independent and only specifiy dialog handshake +at signal level. One should refer to any architecture specific document in +order to manipulate machine dependent registers, mapped memory or whatelse +to control these signals. +.Pp +The IEEE1284 protocol is fully oriented with all supported parallel port +modes. The computer acts as master and the peripheral as slave. +.Pp +Any transfer is defined as a finite state automate. It allows software to +properly manage the fully interlocked scheme of the signaling method. +The compatible mode is supported "as is" without any negociation because it +is compatible. Any other mode must be firstly negociated by the host to check +it is supported by the peripheral, then to enter one of the forward idle +states. +.Pp +At any time, the slave may want to send data to the host. This is only +possible from forward idle states (nibble, byte, ecp...). So, the +host must have previously negociated to permit the peripheral to +request transfer. Interrupt lines may be dedicated to the requesting signals +to prevent time consuming polling methods. +.Pp +But peripheral requests are only a hint to the master host. If the host +accepts the transfer, it must firstly negociate the reverse mode and then +starts the transfer. At any time during reverse transfer, the host may +terminate the transfer or the slave may drive wires to signal that no more +data is available. +.Ss Implementation +IEEE1284 Standard support has been implemented at the top of the ppbus system +as a set of procedures that perform high level functions like negociation, +termination, transfer in any mode without bothering you with low level +caracteristics of the stantdard. +.Pp +IEEE1284 interacts with the ppbus system as least as possible. That means +you still have to request the ppbus when you want to access it, the negociate +function doesn't do it for you. And of course, release it later. .Sh ARCHITECTURE .Ss adapter, ppbus and device layers First, there is the diff --git a/sys/dev/ppbus/immio.c b/sys/dev/ppbus/immio.c index dc37ab3..67054f7 100644 --- a/sys/dev/ppbus/immio.c +++ b/sys/dev/ppbus/immio.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: immio.c,v 1.3 1998/10/02 20:44:58 nsouch Exp $ + * $Id: immio.c,v 1.4 1998/10/31 11:35:21 nsouch Exp $ * */ @@ -47,6 +47,8 @@ #include <sys/kernel.h> #endif /*KERNEL */ +#include "opt_vpo.h" + #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/ppb_msq.h> #include <dev/ppbus/vpoio.h> diff --git a/sys/dev/ppbus/lpbb.c b/sys/dev/ppbus/lpbb.c index 7c77779..cd8ff24 100644 --- a/sys/dev/ppbus/lpbb.c +++ b/sys/dev/ppbus/lpbb.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: lpbb.c,v 1.2 1998/11/09 22:22:01 nsouch Exp $ + * $Id: lpbb.c,v 1.3 1998/12/07 21:58:16 archie Exp $ * */ @@ -240,14 +240,6 @@ else return 0; } -static int getSCL(struct lpbb_softc *sc) -{ -if((ppb_rstr(&sc->lpbb_dev)&SCL_in)==SCL_in) - return 1; -else - return 0; -} - static void setSDA(struct lpbb_softc *sc, char val) { if(val==0) diff --git a/sys/dev/ppbus/nlpt.c b/sys/dev/ppbus/nlpt.c index b39ee64..89d6c7f 100644 --- a/sys/dev/ppbus/nlpt.c +++ b/sys/dev/ppbus/nlpt.c @@ -47,7 +47,7 @@ * * from: unknown origin, 386BSD 0.1 * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp - * $Id: nlpt.c,v 1.10 1998/09/20 14:41:54 nsouch Exp $ + * $Id: nlpt.c,v 1.11 1998/12/04 22:00:33 archie Exp $ */ /* @@ -81,8 +81,11 @@ #endif /*KERNEL*/ #include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_1284.h> #include <dev/ppbus/nlpt.h> +#include "opt_nlpt.h" + #ifndef NLPT_DEBUG #define nlprintf(args) #else @@ -99,6 +102,7 @@ static int volatile nlptflag = 1; #define LPTOUTMAX 1 /* maximal timeout 1 s */ #define LPPRI (PZERO+8) #define BUFSIZE 1024 +#define BUFSTATSIZE 32 #define LPTUNIT(s) ((s)&0x03) #define LPTFLAGS(s) ((s)&0xfc) @@ -163,11 +167,12 @@ DATA_SET(ppbdriver_set, nlptdriver); static d_open_t nlptopen; static d_close_t nlptclose; static d_write_t nlptwrite; +static d_read_t nlptread; static d_ioctl_t nlptioctl; #define CDEV_MAJOR 16 static struct cdevsw nlpt_cdevsw = - { nlptopen, nlptclose, noread, nlptwrite, /*16*/ + { nlptopen, nlptclose, nlptread, nlptwrite, /*16*/ nlptioctl, nullstop, nullreset, nodevtotty, /* lpt */ seltrue, nommap, nostrat, LPT_NAME, NULL, -1 }; @@ -176,6 +181,9 @@ lpt_request_ppbus(struct lpt_data *sc, int how) { int error; + if (sc->sc_state & HAVEBUS) + return (0); + /* we have the bus only if the request succeded */ if ((error = ppb_request_bus(&sc->lpt_dev, how)) == 0) sc->sc_state |= HAVEBUS; @@ -186,13 +194,10 @@ lpt_request_ppbus(struct lpt_data *sc, int how) static int lpt_release_ppbus(struct lpt_data *sc) { - int error; - - /* we do not have the bus only if the request succeeded */ - if ((error = ppb_release_bus(&sc->lpt_dev)) == 0) - sc->sc_state &= ~HAVEBUS; + ppb_release_bus(&sc->lpt_dev); + sc->sc_state &= ~HAVEBUS; - return (error); + return (0); } /* @@ -439,7 +444,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p) struct lpt_data *sc; int s; - int trys; + int trys, err; u_int unit = LPTUNIT(minor(dev)); if ((unit >= nlpt)) @@ -462,9 +467,8 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p) } /* request the ppbus only if we don't have it already */ - if ((sc->sc_state & HAVEBUS) == 0 && - lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) - return (EINTR); + if (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) + return (err); s = spltty(); nlprintf((LPT_NAME " flags 0x%x\n", sc->sc_flags)); @@ -526,6 +530,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p) sc->sc_state = OPEN; sc->sc_inbuf = geteblk(BUFSIZE); + sc->sc_statbuf = geteblk(BUFSTATSIZE); sc->sc_xfercnt = 0; splx(s); @@ -559,8 +564,7 @@ nlptclose(dev_t dev, int flags, int fmt, struct proc *p) if(sc->sc_flags & LP_BYPASS) goto end_close; - if ((sc->sc_state & HAVEBUS) == 0 && - (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR))) + if (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) return (err); sc->sc_state &= ~OPEN; @@ -577,6 +581,7 @@ nlptclose(dev_t dev, int flags, int fmt, struct proc *p) ppb_wctr(&sc->lpt_dev, LPC_NINIT); brelse(sc->sc_inbuf); + brelse(sc->sc_statbuf); end_close: /* release the bus anyway */ @@ -649,6 +654,40 @@ nlpt_pushbytes(struct lpt_data *sc) } /* + * nlptread --retrieve printer status in IEEE1284 NIBBLE mode + */ + +static int +nlptread(dev_t dev, struct uio *uio, int ioflag) +{ + struct lpt_data *sc = lptdata[LPTUNIT(minor(dev))]; + int error = 0, len; + + if ((error = ppb_1284_negociate(&sc->lpt_dev, PPB_NIBBLE, 0))) + return (error); + + /* read data in an other buffer, read/write may be simultaneous */ + len = 0; + while (uio->uio_resid) { + if ((error = ppb_1284_read(&sc->lpt_dev, PPB_NIBBLE, + sc->sc_statbuf->b_data, min(BUFSTATSIZE, + uio->uio_resid), &len))) { + goto error; + } + + if (!len) + goto error; /* no more data */ + + if ((error = uiomove(sc->sc_statbuf->b_data, len, uio))) + goto error; + } + +error: + ppb_1284_terminate(&sc->lpt_dev); + return (error); +} + +/* * nlptwrite --copy a line from user space to a local buffer, then call * putc to get the chars moved to the output queue. * @@ -660,6 +699,7 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag) { register unsigned n; int pl, err; + u_int unit = LPTUNIT(minor(dev)); struct lpt_data *sc = lptdata[LPTUNIT(minor(dev))]; if(sc->sc_flags & LP_BYPASS) { @@ -668,16 +708,36 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag) } /* request the ppbus only if we don't have it already */ - if ((sc->sc_state & HAVEBUS) == 0 && - lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) - return (EINTR); + if (err = lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR)) + return (err); sc->sc_state &= ~INTERRUPTED; while ((n = min(BUFSIZE, uio->uio_resid)) != 0) { sc->sc_cp = sc->sc_inbuf->b_data ; uiomove(sc->sc_cp, n, uio); sc->sc_xfercnt = n ; - while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) { + + if (sc->sc_irq & LP_ENABLE_EXT) { + /* try any extended mode */ + err = ppb_write(&sc->lpt_dev, sc->sc_cp, + sc->sc_xfercnt, 0); + switch (err) { + case 0: + /* if not all data was sent, we could rely + * on polling for the last bytes */ + sc->sc_xfercnt = 0; + break; + case EINTR: + sc->sc_state |= INTERRUPTED; + return(err); + case EINVAL: + /* advanced mode not avail */ + log(LOG_NOTICE, LPT_NAME "%d: advanced mode not avail, polling\n", unit); + break; + default: + return(err); + } + } else while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) { nlprintf(("i")); /* if the printer is ready for a char, */ /* give it one */ @@ -695,6 +755,7 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag) return(err); } } + /* check to see if we must do a polled write */ if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) { nlprintf(("p")); @@ -809,15 +870,35 @@ nlptioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) * this gets syslog'd. */ old_sc_irq = sc->sc_irq; - if(*(int*)data == 0) + switch(*(int*)data) { + case 0: sc->sc_irq &= (~LP_ENABLE_IRQ); - else + break; + case 1: + sc->sc_irq &= (~LP_ENABLE_EXT); sc->sc_irq |= LP_ENABLE_IRQ; + break; + case 2: + /* classic irq based transfer and advanced + * modes are in conflict + */ + sc->sc_irq &= (~LP_ENABLE_IRQ); + sc->sc_irq |= LP_ENABLE_EXT; + break; + case 3: + sc->sc_irq &= (~LP_ENABLE_EXT); + break; + default: + break; + } + if (old_sc_irq != sc->sc_irq ) - log(LOG_NOTICE, LPT_NAME "%d: switched to %s mode\n", + log(LOG_NOTICE, LPT_NAME "%d: switched to %s %s mode\n", unit, (sc->sc_irq & LP_ENABLE_IRQ)? - "interrupt-driven":"polled"); + "interrupt-driven":"polled", + (sc->sc_irq & LP_ENABLE_EXT)? + "extended":"standard"); } else /* polled port */ error = EOPNOTSUPP; break; diff --git a/sys/dev/ppbus/nlpt.h b/sys/dev/ppbus/nlpt.h index b0b3df6..31292b9 100644 --- a/sys/dev/ppbus/nlpt.h +++ b/sys/dev/ppbus/nlpt.h @@ -27,7 +27,7 @@ * @(#)lptreg.h 1.1 (Berkeley) 12/19/90 * Id: lptreg.h,v 1.6 1997/02/22 09:36:52 peter Exp * - * $Id: nlpt.h,v 1.1 1997/08/14 13:57:40 msmith Exp $ + * $Id: nlpt.h,v 1.2 1997/08/16 14:05:32 msmith Exp $ */ #ifndef __NLPT_H #define __NLPT_H @@ -61,13 +61,15 @@ struct lpt_data { #define LP_AUTOLF 0x40 /* tell printer to do an automatic lf */ #define LP_BYPASS 0x80 /* bypass printer ready checks */ struct buf *sc_inbuf; + struct buf *sc_statbuf; short sc_xfercnt ; char sc_primed; char *sc_cp ; - u_char sc_irq ; /* IRQ status of port */ + u_short 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 */ +#define LP_ENABLE_EXT 0x10 /* we shall use advanced mode when possible */ u_char sc_backoff ; /* time to call lptout() again */ #ifdef DEVFS diff --git a/sys/dev/ppbus/ppb_1284.c b/sys/dev/ppbus/ppb_1284.c index 56524d1..96cd6f2 100644 --- a/sys/dev/ppbus/ppb_1284.c +++ b/sys/dev/ppbus/ppb_1284.c @@ -23,11 +23,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_1284.c,v 1.5 1998/09/13 18:26:26 nsouch Exp $ + * $Id: ppb_1284.c,v 1.6 1998/09/13 20:44:55 nsouch Exp $ * */ -#include "opt_debug_1284.h" +/* + * General purpose routines for the IEEE1284-1994 Standard + */ + +#include "opt_ppb_1284.h" #include <sys/param.h> #include <sys/systm.h> @@ -42,24 +46,414 @@ * * Wait for the peripherial up to 40ms */ -int +static int do_1284_wait(struct ppb_device *dev, char mask, char status) { - int i; + return (ppb_poll_device(dev, 4, mask, status, PPB_NOINTR | PPB_POLL)); +} + +static int +do_peripheral_wait(struct ppb_device *dev, char mask, char status) +{ + return (ppb_poll_device(dev, 100, mask, status, PPB_NOINTR | PPB_POLL)); +} + +#define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4) + +/* + * ppb_1284_reset_error() + * + * Unconditionaly reset the error field + */ +static int +ppb_1284_reset_error(struct ppb_device *dev, int state) +{ + dev->ppb->error = PPB_NO_ERROR; + dev->ppb->state = state; + + return (0); +} + +/* + * ppb_1284_get_state() + * + * Get IEEE1284 state + */ +static int +ppb_1284_get_state(struct ppb_device *dev) +{ + return (dev->ppb->state); +} + +/* + * ppb_1284_set_state() + * + * Change IEEE1284 state if no error occured + */ +static int +ppb_1284_set_state(struct ppb_device *dev, int state) +{ + /* call ppb_1284_reset_error() if you absolutly want to change + * the state from PPB_ERROR to another */ + if ((dev->ppb->state != PPB_ERROR) && + (dev->ppb->error == PPB_NO_ERROR)) { + dev->ppb->state = state; + dev->ppb->error = PPB_NO_ERROR; + } + + return (0); +} + +static int +ppb_1284_set_error(struct ppb_device *dev, int error, int event) +{ + /* do not accumulate errors */ + if ((dev->ppb->error == PPB_NO_ERROR) && + (dev->ppb->state != PPB_ERROR)) { + dev->ppb->error = error; + dev->ppb->state = PPB_ERROR; + } + +#ifdef DEBUG_1284 + printf("ppb1284: error=%d status=0x%x event=%d\n", error, + ppb_rstr(dev) & 0xff, event); +#endif + + return (0); +} + +/* + * ppb_request_mode() + * + * Converts mode+options into ext. value + */ +static int +ppb_request_mode(int mode, int options) +{ + int request_mode = 0; + + if (options & PPB_EXTENSIBILITY_LINK) { + request_mode = EXT_LINK_1284_NORMAL; + + } else { + switch (mode) { + case PPB_NIBBLE: + request_mode = (options & PPB_REQUEST_ID) ? + NIBBLE_1284_REQUEST_ID : + NIBBLE_1284_NORMAL; + break; + case PPB_PS2: + request_mode = (options & PPB_REQUEST_ID) ? + BYTE_1284_REQUEST_ID : + BYTE_1284_NORMAL; + break; + case PPB_ECP: + if (options & PPB_USE_RLE) + request_mode = (options & PPB_REQUEST_ID) ? + ECP_1284_RLE_REQUEST_ID : + ECP_1284_RLE; + else + request_mode = (options & PPB_REQUEST_ID) ? + ECP_1284_REQUEST_ID : + ECP_1284_NORMAL; + break; + case PPB_EPP: + request_mode = EPP_1284_NORMAL; + break; + default: + panic("%s: unsupported mode %d\n", __FUNCTION__, mode); + } + } + + return (request_mode); +} + +/* + * ppb_peripheral_negociate() + * + * Negociate the peripheral side + */ +int +ppb_peripheral_negociate(struct ppb_device *dev, int mode, int options) +{ + int spin, request_mode, error = 0; char r; - /* try up to 5ms */ - for (i = 0; i < 20; i++) { - r = ppb_rstr(dev); - DELAY(25); - if ((r & mask) == status) - return (0); + ppb_set_mode(dev, PPB_COMPATIBLE); + ppb_1284_set_state(dev, PPB_PERIPHERAL_NEGOCIATION); + + /* compute ext. value */ + request_mode = ppb_request_mode(mode, options); + + /* wait host */ + spin = 10; + while (spin-- && (ppb_rstr(dev) & nBUSY)) + DELAY(1); + + /* check termination */ + if (!(ppb_rstr(dev) & SELECT) || !spin) { + error = ENODEV; + goto error; + } + + /* Event 4 - read ext. value */ + r = ppb_rdtr(dev); + + /* nibble mode is not supported */ + if ((r == (char)request_mode) || + (r == NIBBLE_1284_NORMAL)) { + + /* Event 5 - restore direction bit, no data avail */ + ppb_wctr(dev, (STROBE | nINIT) & ~(SELECTIN)); + DELAY(1); + + /* Event 6 */ + ppb_wctr(dev, (nINIT) & ~(SELECTIN | STROBE)); + + if (r == NIBBLE_1284_NORMAL) { +#ifdef DEBUG_1284 + printf("R"); +#endif + ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 4); + error = EINVAL; + goto error; + } else { + ppb_1284_set_state(dev, PPB_PERIPHERAL_IDLE); + switch (r) { + case BYTE_1284_NORMAL: + ppb_set_mode(dev, PPB_BYTE); + break; + default: + break; + } +#ifdef DEBUG_1284 + printf("A"); +#endif + /* negociation succeeds */ + } + } else { + /* Event 5 - mode not supported */ + ppb_wctr(dev, SELECTIN); + DELAY(1); + + /* Event 6 */ + ppb_wctr(dev, (SELECTIN) & ~(STROBE | nINIT)); + ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 4); + +#ifdef DEBUG_1284 + printf("r"); +#endif + error = EINVAL; + goto error; } - return (ppb_poll_device(dev, 4, mask, status, PPB_NOINTR)); + return (0); + +error: + ppb_peripheral_terminate(dev, PPB_WAIT); + return (error); } -#define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4) +/* + * ppb_peripheral_terminate() + * + * Terminate peripheral transfer side + * + * Always return 0 in compatible mode + */ +int +ppb_peripheral_terminate(struct ppb_device *dev, int how) +{ + int error = 0; + +#ifdef DEBUG_1284 + printf("t"); +#endif + + ppb_1284_set_state(dev, PPB_PERIPHERAL_TERMINATION); + + /* Event 22 - wait up to host response time (1s) */ + if ((error = do_peripheral_wait(dev, SELECT | nBUSY, 0))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 22); + goto error; + } + + /* Event 24 */ + ppb_wctr(dev, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN)); + + /* Event 25 - wait up to host response time (1s) */ + if ((error = do_peripheral_wait(dev, nBUSY, nBUSY))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 25); + goto error; + } + + /* Event 26 */ + ppb_wctr(dev, (SELECTIN | nINIT | STROBE) & ~(AUTOFEED)); + DELAY(1); + /* Event 27 */ + ppb_wctr(dev, (SELECTIN | nINIT) & ~(STROBE | AUTOFEED)); + + /* Event 28 - wait up to host response time (1s) */ + if ((error = do_peripheral_wait(dev, nBUSY, 0))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 28); + goto error; + } + +error: + ppb_set_mode(dev, PPB_COMPATIBLE); + ppb_1284_set_state(dev, PPB_FORWARD_IDLE); + + return (0); +} + +/* + * byte_peripheral_outbyte() + * + * Write 1 byte in BYTE mode + */ +static int +byte_peripheral_outbyte(struct ppb_device *dev, char *buffer, int last) +{ + int error = 0; + + /* Event 7 */ + if ((error = do_1284_wait(dev, nBUSY, nBUSY))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 7); + goto error; + } + + /* check termination */ + if (!(ppb_rstr(dev) & SELECT)) { + ppb_peripheral_terminate(dev, PPB_WAIT); + goto error; + } + + /* Event 15 - put byte on data lines */ +#ifdef DEBUG_1284 + printf("B"); +#endif + ppb_wdtr(dev, *buffer); + + /* Event 9 */ + ppb_wctr(dev, (AUTOFEED | STROBE) & ~(nINIT | SELECTIN)); + + /* Event 10 - wait data read */ + if ((error = do_peripheral_wait(dev, nBUSY, 0))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 16); + goto error; + } + + /* Event 11 */ + if (!last) { + ppb_wctr(dev, (AUTOFEED) & ~(nINIT | STROBE | SELECTIN)); + } else { + ppb_wctr(dev, (nINIT) & ~(STROBE | SELECTIN | AUTOFEED)); + } + +#if 0 + /* Event 16 - wait strobe */ + if ((error = do_peripheral_wait(dev, nACK | nBUSY, 0))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 16); + goto error; + } +#endif + + /* check termination */ + if (!(ppb_rstr(dev) & SELECT)) { + ppb_peripheral_terminate(dev, PPB_WAIT); + goto error; + } + +error: + return (error); +} + +/* + * byte_peripheral_write() + * + * Write n bytes in BYTE mode + */ +int +byte_peripheral_write(struct ppb_device *dev, char *buffer, int len, int *sent) +{ + int error = 0, i; + char r; + + ppb_1284_set_state(dev, PPB_PERIPHERAL_TRANSFER); + + /* wait forever, the remote host is master and should initiate + * termination + */ + for (i=0; i<len; i++) { + /* force remote nFAULT low to release the remote waiting + * process, if any + */ + r = ppb_rctr(dev); + ppb_wctr(dev, r & ~nINIT); + +#ifdef DEBUG_1284 + printf("y"); +#endif + /* Event 7 */ + error = ppb_poll_device(dev, PPB_FOREVER, nBUSY, nBUSY, + PPB_INTR); + + if (error && error != EWOULDBLOCK) + goto error; + +#ifdef DEBUG_1284 + printf("b"); +#endif + if ((error = byte_peripheral_outbyte(dev, buffer+i, (i == len-1)))) + goto error; + } +error: + if (!error) + ppb_1284_set_state(dev, PPB_PERIPHERAL_IDLE); + + *sent = i; + return (error); +} + +/* + * byte_1284_inbyte() + * + * Read 1 byte in BYTE mode + */ +int +byte_1284_inbyte(struct ppb_device *dev, char *buffer) +{ + int error = 0; + + /* Event 7 - ready to take data (nAUTO low) */ + ppb_wctr(dev, (PCD | nINIT | AUTOFEED) & ~(STROBE | SELECTIN)); + + /* Event 9 - peripheral set nAck low */ + if ((error = do_1284_wait(dev, nACK, 0))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 9); + goto error; + } + + /* read the byte */ + *buffer = ppb_rdtr(dev); + + /* Event 10 - data received, can't accept more */ + ppb_wctr(dev, (nINIT) & ~(AUTOFEED | STROBE | SELECTIN)); + + /* Event 11 - peripheral ack */ + if ((error = do_1284_wait(dev, nACK, nACK))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 11); + goto error; + } + + /* Event 16 - strobe */ + ppb_wctr(dev, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN)); + DELAY(3); + ppb_wctr(dev, (nINIT) & ~(AUTOFEED | STROBE | SELECTIN)); + +error: + return (error); +} /* * nibble_1284_inbyte() @@ -73,126 +467,372 @@ nibble_1284_inbyte(struct ppb_device *dev, char *buffer) int i, error; for (i = 0; i < 2; i++) { - /* ready to take data (nAUTO low) */ - ppb_wctr(dev, AUTOFEED & ~(STROBE | SELECTIN)); - if ((error = do_1284_wait(dev, nACK, 0))) - return (error); + /* Event 7 - ready to take data (nAUTO low) */ + ppb_wctr(dev, (nINIT | AUTOFEED) & ~(STROBE | SELECTIN)); + + /* Event 8 - peripheral writes the first nibble */ + + /* Event 9 - peripheral set nAck low */ + if ((error = do_1284_wait(dev, nACK, 0))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 9); + goto error; + } /* read nibble */ nibble[i] = ppb_rstr(dev); - /* ack, not ready for another nibble */ - ppb_wctr(dev, 0 & ~(AUTOFEED | STROBE | SELECTIN)); + /* Event 10 - ack, nibble received */ + ppb_wctr(dev, nINIT & ~(AUTOFEED | STROBE | SELECTIN)); - /* wait ack from peripherial */ - if ((error = do_1284_wait(dev, nACK, nACK))) - return (error); + /* Event 11 - wait ack from peripherial */ + if ((error = do_1284_wait(dev, nACK, nACK))) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 11); + goto error; + } } *buffer = ((nibble2char(nibble[1]) << 4) & 0xf0) | (nibble2char(nibble[0]) & 0x0f); - return (0); +error: + return (error); +} + +/* + * spp_1284_read() + * + * Read in IEEE1284 NIBBLE/BYTE mode + */ +int +spp_1284_read(struct ppb_device *dev, int mode, char *buffer, int max, int *read) +{ + int error = 0, len = 0; + int terminate_after_transfer = 1; + int state; + + *read = len = 0; + + state = ppb_1284_get_state(dev); + + switch (state) { + case PPB_FORWARD_IDLE: + if ((error = ppb_1284_negociate(dev, mode, 0))) + return (error); + break; + + case PPB_REVERSE_IDLE: + terminate_after_transfer = 0; + break; + + default: + ppb_1284_terminate(dev); + if ((error = ppb_1284_negociate(dev, mode, 0))) + return (error); + break; + } + + while ((len < max) && !(ppb_rstr(dev) & (nFAULT))) { + + ppb_1284_set_state(dev, PPB_REVERSE_TRANSFER); + +#ifdef DEBUG_1284 + printf("B"); +#endif + + switch (mode) { + case PPB_NIBBLE: + /* read a byte, error means no more data */ + if (nibble_1284_inbyte(dev, buffer+len)) + goto end_while; + break; + case PPB_BYTE: + if (byte_1284_inbyte(dev, buffer+len)) + goto end_while; + break; + default: + error = EINVAL; + goto end_while; + } + len ++; + } +end_while: + + if (!error) + ppb_1284_set_state(dev, PPB_REVERSE_IDLE); + + *read = len; + + if (terminate_after_transfer || error) + ppb_1284_terminate(dev); + + return (error); } /* - * nibble_1284_sync() + * ppb_1284_read_id() + * */ -void -nibble_1284_sync(struct ppb_device *dev) +int +ppb_1284_read_id(struct ppb_device *dev, int mode, char *buffer, + int max, int *read) { - char ctr; + int error = 0; - ctr = ppb_rctr(dev); + /* fill the buffer with 0s */ + bzero(buffer, max); - ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN); - if (do_1284_wait(dev, nACK, 0)) - return; + switch (mode) { + case PPB_NIBBLE: + case PPB_ECP: + if ((error = ppb_1284_negociate(dev, PPB_NIBBLE, PPB_REQUEST_ID))) + return (error); + error = spp_1284_read(dev, PPB_NIBBLE, buffer, max, read); + break; + case PPB_BYTE: + if ((error = ppb_1284_negociate(dev, PPB_BYTE, PPB_REQUEST_ID))) + return (error); + error = spp_1284_read(dev, PPB_BYTE, buffer, max, read); + break; + default: + panic("%s: unsupported mode %d\n", __FUNCTION__, mode); + } - ppb_wctr(dev, ctr | AUTOFEED); - do_1284_wait(dev, nACK, nACK); + ppb_1284_terminate(dev); + return (error); +} - ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN); +/* + * ppb_1284_read() + * + * IEEE1284 read + */ +int +ppb_1284_read(struct ppb_device *dev, int mode, char *buffer, + int max, int *read) +{ + int error = 0; - return; + switch (mode) { + case PPB_NIBBLE: + case PPB_BYTE: + error = spp_1284_read(dev, mode, buffer, max, read); + break; + default: + return (EINVAL); + } + + return (error); } /* * ppb_1284_negociate() * + * IEEE1284 negociation phase + * * Normal nibble mode or request device id mode (see ppb_1284.h) + * + * After negociation, nFAULT is low if data is available */ int -ppb_1284_negociate(struct ppb_device *dev, int mode) +ppb_1284_negociate(struct ppb_device *dev, int mode, int options) { int error; - int phase = 0; + int request_mode; + +#ifdef DEBUG_1284 + printf("n"); +#endif + + if (ppb_1284_get_state(dev) >= PPB_PERIPHERAL_NEGOCIATION) + ppb_peripheral_terminate(dev, PPB_WAIT); + if (ppb_1284_get_state(dev) != PPB_FORWARD_IDLE) + ppb_1284_terminate(dev); + +#ifdef DEBUG_1284 + printf("%d", mode); +#endif + + /* ensure the host is in compatible mode */ + ppb_set_mode(dev, PPB_COMPATIBLE); + + /* reset error to catch the actual negociation error */ + ppb_1284_reset_error(dev, PPB_FORWARD_IDLE); + + /* calculate ext. value */ + request_mode = ppb_request_mode(mode, options); + + /* default state */ ppb_wctr(dev, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED)); DELAY(1); - ppb_wdtr(dev, mode); + /* enter negociation phase */ + ppb_1284_set_state(dev, PPB_NEGOCIATION); + + /* Event 0 - put the exten. value on the data lines */ + ppb_wdtr(dev, request_mode); + +#ifdef PERIPH_1284 + /* request remote host attention */ + ppb_wctr(dev, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN)); + DELAY(1); + ppb_wctr(dev, (nINIT) & ~(STROBE | AUTOFEED | SELECTIN)); +#else DELAY(1); +#endif /* !PERIPH_1284 */ + + /* Event 1 - enter IEEE1284 mode */ ppb_wctr(dev, (nINIT | AUTOFEED) & ~(STROBE | SELECTIN)); - if ((error = do_1284_wait(dev, nACK | PERROR | SELECT | nFAULT, - PERROR | SELECT | nFAULT))) +#ifdef PERIPH_1284 + /* ignore the PError line, wait a bit more, remote host's + * interrupts don't respond fast enough */ + if (ppb_poll_device(dev, 40, nACK | SELECT | nFAULT, + SELECT | nFAULT, PPB_NOINTR | PPB_POLL)) { + ppb_1284_set_error(dev, PPB_NOT_IEEE1284, 2); + error = ENODEV; + goto error; + } +#else + /* Event 2 - trying IEEE1284 dialog */ + if (do_1284_wait(dev, nACK | PERROR | SELECT | nFAULT, + PERROR | SELECT | nFAULT)) { + ppb_1284_set_error(dev, PPB_NOT_IEEE1284, 2); + error = ENODEV; goto error; + } +#endif /* !PERIPH_1284 */ - phase = 1; - + /* Event 3 - latch the ext. value to the peripheral */ ppb_wctr(dev, (nINIT | STROBE | AUTOFEED) & ~SELECTIN); - DELAY(5); + DELAY(1); + /* Event 4 - IEEE1284 device recognized */ ppb_wctr(dev, nINIT & ~(SELECTIN | AUTOFEED | STROBE)); -#if 0 /* not respected by most devices */ - if ((error = do_1284_wait(dev, nACK, nACK))) + /* Event 6 - waiting for status lines */ + if (do_1284_wait(dev, nACK, nACK)) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 6); + error = EBUSY; goto error; + } - if (mode == 0) - if ((error = do_1284_wait(dev, SELECT, 0))) + /* Event 7 - quering result consider nACK not to misunderstand + * a remote computer terminate sequence */ + if (request_mode == NIBBLE_1284_NORMAL) { + if (do_1284_wait(dev, nACK | SELECT, nACK)) { + ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 7); + error = ENODEV; goto error; - else - if ((error = do_1284_wait(dev, SELECT, SELECT))) + } + } else { + if (do_1284_wait(dev, nACK | SELECT, SELECT | nACK)) { + ppb_1284_set_error(dev, PPB_MODE_UNSUPPORTED, 7); + error = ENODEV; goto error; -#endif + } + } + + switch (mode) { + case PPB_NIBBLE: + case PPB_PS2: + /* enter reverse idle phase */ + ppb_1284_set_state(dev, PPB_REVERSE_IDLE); + break; + case PPB_ECP: + /* negociation ok, now setup the communication */ + ppb_1284_set_state(dev, PPB_SETUP); + ppb_wctr(dev, (nINIT | AUTOFEED) & ~(SELECTIN | STROBE)); + +#ifdef PERIPH_1284 + /* ignore PError line */ + if (do_1284_wait(dev, nACK | SELECT | nBUSY, + nACK | SELECT | nBUSY)) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 30); + error = ENODEV; + goto error; + } +#else + if (do_1284_wait(dev, nACK | SELECT | PERROR | nBUSY, + nACK | SELECT | PERROR | nBUSY)) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 30); + error = ENODEV; + goto error; + } +#endif /* !PERIPH_1284 */ + + /* ok, the host enters the ForwardIdle state */ + ppb_1284_set_state(dev, PPB_ECP_FORWARD_IDLE); + break; + case PPB_EPP: + ppb_1284_set_state(dev, PPB_EPP_IDLE); + break; + + default: + panic("%s: unknown mode (%d)!", __FUNCTION__, mode); + } + ppb_set_mode(dev, mode); return (0); error: - if (bootverbose) - printf("%s: status=0x%x %d\n", __FUNCTION__, ppb_rstr(dev), phase); + ppb_1284_terminate(dev); return (error); } +/* + * ppb_1284_terminate() + * + * IEEE1284 termination phase, return code should ignored since the host + * is _always_ in compatible mode after ppb_1284_terminate() + */ int -ppb_1284_terminate(struct ppb_device *dev, int how) +ppb_1284_terminate(struct ppb_device *dev) { - int error; - switch (how) { - case VALID_STATE: +#ifdef DEBUG_1284 + printf("T"); +#endif - ppb_wctr(dev, SELECTIN & ~(STROBE | AUTOFEED)); + /* do not reset error here to keep the error that + * may occured before the ppb_1284_terminate() call */ + ppb_1284_set_state(dev, PPB_TERMINATION); - if ((error = do_1284_wait(dev, nACK | nBUSY | nFAULT, nFAULT))) - return (error); +#ifdef PERIPH_1284 + /* request remote host attention */ + ppb_wctr(dev, (nINIT | STROBE | SELECTIN) & ~(AUTOFEED)); + DELAY(1); +#endif /* PERIPH_1284 */ - ppb_wctr(dev, (SELECTIN | AUTOFEED) & ~STROBE); + /* Event 22 - set nSelectin low and nAutoFeed high */ + ppb_wctr(dev, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED)); - if ((error = do_1284_wait(dev, nACK, nACK))) - return (error); + /* Event 24 - waiting for peripheral, Xflag ignored */ + if (do_1284_wait(dev, nACK | nBUSY | nFAULT, nFAULT)) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 24); + goto error; + } - ppb_wctr(dev, SELECTIN & ~(STROBE | AUTOFEED)); - break; + /* Event 25 - set nAutoFd low */ + ppb_wctr(dev, (nINIT | SELECTIN | AUTOFEED) & ~STROBE); - default: - return (EINVAL); + /* Event 26 - compatible mode status is set */ + + /* Event 27 - peripheral set nAck high */ + if (do_1284_wait(dev, nACK, nACK)) { + ppb_1284_set_error(dev, PPB_TIMEOUT, 27); } + /* Event 28 - end termination, return to idle phase */ + ppb_wctr(dev, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED)); + +error: + /* return to compatible mode */ + ppb_set_mode(dev, PPB_COMPATIBLE); + ppb_1284_set_state(dev, PPB_FORWARD_IDLE); + return (0); } diff --git a/sys/dev/ppbus/ppb_1284.h b/sys/dev/ppbus/ppb_1284.h index 1cc98d4..ea009b7d 100644 --- a/sys/dev/ppbus/ppb_1284.h +++ b/sys/dev/ppbus/ppb_1284.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_1284.h,v 1.2 1998/08/03 19:14:31 msmith Exp $ + * $Id: ppb_1284.h,v 1.3 1998/09/13 18:26:26 nsouch Exp $ * */ #ifndef __1284_H @@ -66,20 +66,59 @@ #define Intr nACK /* request mode values */ -#define NIBBLE_1284_NORMAL 0 -#define NIBBLE_1284_REQUEST_ID 4 - -/* how to terminate */ -#define VALID_STATE 0 -#define IMMEDIATE 1 - -extern int do_1284_wait(struct ppb_device *, char, char); +#define NIBBLE_1284_NORMAL 0x0 +#define NIBBLE_1284_REQUEST_ID 0x4 +#define BYTE_1284_NORMAL 0x1 +#define BYTE_1284_REQUEST_ID 0x5 +#define ECP_1284_NORMAL 0x10 +#define ECP_1284_REQUEST_ID 0x14 +#define ECP_1284_RLE 0x30 +#define ECP_1284_RLE_REQUEST_ID 0x34 +#define EPP_1284_NORMAL 0x40 +#define EXT_LINK_1284_NORMAL 0x80 + +/* ieee1284 mode options */ +#define PPB_REQUEST_ID 0x1 +#define PPB_USE_RLE 0x2 +#define PPB_EXTENSIBILITY_LINK 0x4 + +/* ieee1284 errors */ +#define PPB_NO_ERROR 0 +#define PPB_MODE_UNSUPPORTED 1 /* mode not supported by peripheral */ +#define PPB_NOT_IEEE1284 2 /* not an IEEE1284 compliant periph. */ +#define PPB_TIMEOUT 3 /* timeout */ +#define PPB_INVALID_MODE 4 /* current mode is incorrect */ + +/* ieee1284 host side states */ +#define PPB_ERROR 0 +#define PPB_FORWARD_IDLE 1 +#define PPB_NEGOCIATION 2 +#define PPB_SETUP 3 +#define PPB_ECP_FORWARD_IDLE 4 +#define PPB_FWD_TO_REVERSE 5 +#define PPB_REVERSE_IDLE 6 +#define PPB_REVERSE_TRANSFER 7 +#define PPB_REVERSE_TO_FWD 8 +#define PPB_EPP_IDLE 9 +#define PPB_TERMINATION 10 + +/* peripheral side states */ +#define PPB_PERIPHERAL_NEGOCIATION 11 +#define PPB_PERIPHERAL_IDLE 12 +#define PPB_PERIPHERAL_TRANSFER 13 +#define PPB_PERIPHERAL_TERMINATION 14 extern int nibble_1284_inbyte(struct ppb_device *, char *); -extern void nibble_1284_sync(struct ppb_device *); -extern int nibble_1284_mode(struct ppb_device *, int); +extern int byte_1284_inbyte(struct ppb_device *, char *); +extern int spp_1284_read(struct ppb_device *, int, char *, int, int *); + +extern int ppb_1284_negociate(struct ppb_device *, int, int); +extern int ppb_1284_terminate(struct ppb_device *); +extern int ppb_1284_read_id(struct ppb_device *, int, char *, int, int *); +extern int ppb_1284_read(struct ppb_device *, int, char *, int, int *); -extern int ppb_1284_negociate(struct ppb_device *, int); -extern int ppb_1284_terminate(struct ppb_device *, int how); +extern int ppb_peripheral_terminate(struct ppb_device *, int); +extern int ppb_peripheral_negociate(struct ppb_device *, int, int); +extern int byte_peripheral_write(struct ppb_device *, char *, int, int *); #endif diff --git a/sys/dev/ppbus/ppb_base.c b/sys/dev/ppbus/ppb_base.c index 7555b24..83fc26e 100644 --- a/sys/dev/ppbus/ppb_base.c +++ b/sys/dev/ppbus/ppb_base.c @@ -23,12 +23,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_base.c,v 1.4 1998/08/03 19:14:31 msmith Exp $ + * $Id: ppb_base.c,v 1.5 1998/09/13 18:26:26 nsouch Exp $ * */ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <machine/clock.h> #include <dev/ppbus/ppbconf.h> @@ -70,28 +71,39 @@ int ppb_poll_device(struct ppb_device *dev, int max, char mask, char status, int how) { - int i, error; + int i, j, error; + char r; + + /* try at least up to 10ms */ + for (j = 0; j < ((how & PPB_POLL) ? max : 1); j++) { + for (i = 0; i < 10000; i++) { + r = ppb_rstr(dev); + DELAY(1); + if ((r & mask) == status) + return (0); + } + } - for (i = 0; i < max; i++) { + if (!(how & PPB_POLL)) { + for (i = 0; max == PPB_FOREVER || i < max-1; i++) { if ((ppb_rstr(dev) & mask) == status) return (0); switch (how) { case PPB_NOINTR: /* wait 10 ms */ - if ((error = tsleep((caddr_t)dev, PPBPRI, - "ppbpoll", hz/100))) - return (error); + tsleep((caddr_t)dev, PPBPRI, "ppbpoll", hz/100); break; case PPB_INTR: default: /* wait 10 ms */ if ((error = tsleep((caddr_t)dev, PPBPRI | PCATCH, - "ppbpoll", hz/100))) + "ppbpoll", hz/100)) != EWOULDBLOCK) return (error); break; } + } } return (EWOULDBLOCK); @@ -108,16 +120,31 @@ ppb_set_mode(struct ppb_device *dev, int mode) struct ppb_data *ppb = dev->ppb; int old_mode = ppb_get_mode(dev); - if ((*ppb->ppb_link->adapter->setmode)(dev->id_unit, mode)) + if ((*ppb->ppb_link->adapter->setmode)( + ppb->ppb_link->adapter_unit, mode)) return (-1); /* XXX yet device mode = ppbus mode = chipset mode */ - dev->mode = ppb->mode = mode; + dev->mode = ppb->mode = (mode & PPB_MASK); return (old_mode); } /* + * ppb_write() + * + * Write charaters to the port + */ +int +ppb_write(struct ppb_device *dev, char *buf, int len, int how) +{ + struct ppb_data *ppb = dev->ppb; + + return (ppb->ppb_link->adapter->write(ppb->ppb_link->adapter_unit, + buf, len, how)); +} + +/* * ppb_reset_epp_timeout() * * Reset the EPP timeout bit in the status register @@ -130,7 +157,7 @@ ppb_reset_epp_timeout(struct ppb_device *dev) if (ppb->ppb_owner != dev) return (EACCES); - (*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit); + (*ppb->ppb_link->adapter->reset_epp_timeout)(ppb->ppb_link->adapter_unit); return (0); } @@ -148,7 +175,7 @@ ppb_ecp_sync(struct ppb_device *dev) if (ppb->ppb_owner != dev) return (EACCES); - (*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit); + (*ppb->ppb_link->adapter->ecp_sync)(ppb->ppb_link->adapter_unit); return (0); } diff --git a/sys/dev/ppbus/ppb_msq.c b/sys/dev/ppbus/ppb_msq.c index 7e631e4..addcf04 100644 --- a/sys/dev/ppbus/ppb_msq.c +++ b/sys/dev/ppbus/ppb_msq.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_msq.c,v 1.2 1998/09/13 18:26:26 nsouch Exp $ + * $Id: ppb_msq.c,v 1.3 1998/09/20 14:41:54 nsouch Exp $ * */ #include <machine/stdarg.h> @@ -272,9 +272,19 @@ ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret) xfer = mode2xfer(dev, mi->opcode); /* figure out if we should use ieee1284 code */ - if (!xfer->loop) - panic("%s: IEEE1284 code not supported", - __FUNCTION__); + if (!xfer->loop) { + if (mi->opcode == MS_OP_PUT) { + if ((error = ppb->ppb_link->adapter->write( + ppb->ppb_link->adapter_unit, + (char *)mi->arg[0].p, + mi->arg[1].i, 0))) + goto error; + + INCR_PC; + goto next; + } else + panic("%s: IEEE1284 read not supported", __FUNCTION__); + } /* XXX should use ppb_MS_init_msq() */ initxfer[0].arg[0].p = mi->arg[0].p; @@ -309,10 +319,12 @@ ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret) * is unknown here */ if ((error = ppb->ppb_link->adapter->exec_microseq( - dev->id_unit, &mi))) + ppb->ppb_link->adapter_unit, + &mi))) goto error; break; } + next: } error: return (error); diff --git a/sys/dev/ppbus/ppb_msq.h b/sys/dev/ppbus/ppb_msq.h index cdc8154..4bba299 100644 --- a/sys/dev/ppbus/ppb_msq.h +++ b/sys/dev/ppbus/ppb_msq.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppb_msq.h,v 1.1.2.7 1998/06/20 19:03:47 son Exp $ + * $Id: ppb_msq.h,v 1.2 1998/09/13 18:26:26 nsouch Exp $ * */ #ifndef __PPB_MSQ_H @@ -34,7 +34,7 @@ */ /* microsequence parameter descriptor */ -#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position */ +#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position < 256 XXX */ #define MS_ARG_MASK 0x0f00 /* mask to retrieve the argument number */ #define MS_TYP_MASK 0xf000 /* mask to retrieve the type of the param */ diff --git a/sys/dev/ppbus/ppbconf.c b/sys/dev/ppbus/ppbconf.c index 8f657cd..779db99 100644 --- a/sys/dev/ppbus/ppbconf.c +++ b/sys/dev/ppbus/ppbconf.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1997, 1998 Nicolas Souchu + * Copyright (c) 1997, 1998, 1999 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppbconf.c,v 1.8 1998/09/20 14:41:54 nsouch Exp $ + * $Id: ppbconf.c,v 1.9 1998/12/07 21:58:16 archie Exp $ * */ #include <sys/param.h> @@ -37,6 +37,8 @@ #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/ppb_1284.h> +#include "opt_ppb_1284.h" + LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */ /* @@ -80,15 +82,30 @@ ppb_alloc_bus(void) return(ppb); } +#define PPB_PNP_PRINTER 0 +#define PPB_PNP_MODEM 1 +#define PPB_PNP_NET 2 +#define PPB_PNP_HDC 3 +#define PPB_PNP_PCMCIA 4 +#define PPB_PNP_MEDIA 5 +#define PPB_PNP_FDC 6 +#define PPB_PNP_PORTS 7 +#define PPB_PNP_SCANNER 8 +#define PPB_PNP_DIGICAM 9 + +#ifndef DONTPROBE_1284 + static char *pnp_tokens[] = { "PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA", "FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL }; +#if 0 static char *pnp_classes[] = { "printer", "modem", "network device", "hard disk", "PCMCIA", "multimedia device", "floppy disk", "ports", "scanner", "digital camera", "unknown device", NULL }; +#endif /* * search_token() @@ -134,75 +151,40 @@ search_token(char *str, int slen, char *token) * Returns the class id. of the peripherial, -1 otherwise */ static int -ppb_pnp_detect(struct ppb_data *ppb) +ppb_pnp_detect(struct ppb_data *ppb, struct ppb_device *pnpdev) { - char *token, *q, *class = 0; + char *token, *class = 0; int i, len, error; int class_id = -1; char str[PPB_PnP_STRING_SIZE+1]; - struct ppb_device pnpdev; /* temporary device to perform I/O */ - - /* initialize the pnpdev structure for future use */ - bzero(&pnpdev, sizeof(pnpdev)); - - pnpdev.ppb = ppb; - if (bootverbose) - printf("ppb: <PnP> probing devices on ppbus %d...\n", + printf("Probing for PnP devices on ppbus%d:\n", ppb->ppb_link->adapter_unit); - - if (ppb_request_bus(&pnpdev, PPB_DONTWAIT)) { - if (bootverbose) - printf("ppb: <PnP> cannot allocate ppbus!\n"); - return (-1); - } - - if ((error = ppb_1284_negociate(&pnpdev, NIBBLE_1284_REQUEST_ID))) { - if (bootverbose) - printf("ppb: <PnP> ppb_1284_negociate()=%d\n", error); - - goto end_detect; - } - len = 0; - for (q=str; !(ppb_rstr(&pnpdev) & PERROR); q++) { - if ((error = nibble_1284_inbyte(&pnpdev, q))) { - if (bootverbose) { - *q = '\0'; - printf("ppb: <PnP> len=%d, %s\n", len, str); - printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", - error); - } - goto end_detect; - } - - if (len++ >= PPB_PnP_STRING_SIZE) { - printf("ppb: <PnP> not space left!\n"); - goto end_detect; - } - } - *q = '\0'; - - nibble_1284_sync(&pnpdev); + if ((error = ppb_1284_read_id(pnpdev, PPB_NIBBLE, str, + PPB_PnP_STRING_SIZE, &len))) + goto end_detect; - if (bootverbose) { - printf("ppb: <PnP> %d characters: ", len); - for (i = 0; i < len; i++) - printf("0x%x ", str[i]); - printf("\n"); - } +#ifdef DEBUG_1284 + printf("ppb: <PnP> %d characters: ", len); + for (i = 0; i < len; i++) + printf("%c(0x%x) ", str[i], str[i]); + printf("\n"); +#endif /* replace ';' characters by '\0' */ for (i = 0; i < len; i++) str[i] = (str[i] == ';') ? '\0' : str[i]; - if ((token = search_token(str, len, "MFG")) != NULL) + if ((token = search_token(str, len, "MFG")) != NULL || + (token = search_token(str, len, "MANUFACTURER")) != NULL) printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit, search_token(token, UNKNOWN_LENGTH, ":") + 1); else printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit); - if ((token = search_token(str, len, "MDL")) != NULL) + if ((token = search_token(str, len, "MDL")) != NULL || + (token = search_token(str, len, "MODEL")) != NULL) printf(" %s", search_token(token, UNKNOWN_LENGTH, ":") + 1); else @@ -223,7 +205,8 @@ ppb_pnp_detect(struct ppb_data *ppb) printf(" %s", class); } - if ((token = search_token(str, len, "CMD")) != NULL) + if ((token = search_token(str, len, "CMD")) != NULL || + (token = search_token(str, len, "COMMAND")) != NULL) printf(" %s", search_token(token, UNKNOWN_LENGTH, ":") + 1); @@ -241,13 +224,116 @@ ppb_pnp_detect(struct ppb_data *ppb) class_id = PPB_PnP_UNKNOWN; end_detect: - if ((error = ppb_1284_terminate(&pnpdev, VALID_STATE)) && bootverbose) - printf("ppb: ppb_1284_terminate()=%d\n", error); + return (class_id); +} + +/* + * ppb_scan_bus() + * + * Scan the ppbus for IEEE1284 compliant devices + */ +static int +ppb_scan_bus(struct ppb_data *ppb) +{ + struct ppb_device pnpdev; /* temporary device to perform I/O */ + int error = 0; + + /* initialize the pnpdev structure for future use */ + bzero(&pnpdev, sizeof(pnpdev)); + pnpdev.ppb = ppb; + + if ((error = ppb_request_bus(&pnpdev, PPB_DONTWAIT))) { + if (bootverbose) + printf("ppb: cannot allocate ppbus!\n"); + + return (error); + } + + /* try all IEEE1284 modes, for one device only + * + * XXX We should implement the IEEE1284.3 standard to detect + * daisy chained devices + */ + + error = ppb_1284_negociate(&pnpdev, PPB_NIBBLE, PPB_REQUEST_ID); + + if ((ppb->state == PPB_ERROR) && (ppb->error == PPB_NOT_IEEE1284)) + goto end_scan; + + ppb_1284_terminate(&pnpdev); + + printf("ppc%d: IEEE1284 device found ", ppb->ppb_link->adapter_unit); + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_NIBBLE, 0))) { + printf("/NIBBLE"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_PS2, 0))) { + printf("/PS2"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, 0))) { + printf("/ECP"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, PPB_USE_RLE))) { + printf("/ECP_RLE"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_EPP, 0))) { + printf("/EPP"); + ppb_1284_terminate(&pnpdev); + } + +#if 0 + if (!(error = ppb_1284_negociate(&pnpdev, PPB_NIBBLE, PPB_REQUEST_ID))) { + printf("/NIBBLE_ID"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_PS2, PPB_REQUEST_ID))) { + printf("/PS2_ID"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, PPB_REQUEST_ID))) { + printf("/ECP_ID"); + ppb_1284_terminate(&pnpdev); + } + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_ECP, + PPB_REQUEST_ID | PPB_USE_RLE))) { + printf("/ECP_RLE_ID"); + ppb_1284_terminate(&pnpdev); + } +#endif + + if (!(error = ppb_1284_negociate(&pnpdev, PPB_COMPATIBLE, + PPB_EXTENSIBILITY_LINK))) { + printf("/Extensibility Link"); + ppb_1284_terminate(&pnpdev); + } + + printf(" in FORWARD_IDLE state\n"); + + /* detect PnP devices */ + ppb->class_id = ppb_pnp_detect(ppb, &pnpdev); ppb_release_bus(&pnpdev); - return (class_id); + + return (0); + +end_scan: + ppb_release_bus(&pnpdev); + return (error); } +#endif /* !DONTPROBE_1284 */ + /* * ppb_attachdevs() * @@ -263,11 +349,10 @@ ppb_attachdevs(struct ppb_data *ppb) LIST_INIT(&ppb->ppb_devs); /* initialise device/driver list */ p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items; -/* XXX wait for ieee1284 good support */ -#if 0 - /* detect PnP devices */ - ppb->class_id = ppb_pnp_detect(ppb); -#endif +#ifndef DONTPROBE_1284 + /* detect IEEE1284 compliant devices */ + ppb_scan_bus(ppb); +#endif /* !DONTPROBE_1284 */ /* * Blindly try all probes here. Later we should look at diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h index 80efe28..80bfa7b 100644 --- a/sys/dev/ppbus/ppbconf.h +++ b/sys/dev/ppbus/ppbconf.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppbconf.h,v 1.8 1998/09/13 18:26:26 nsouch Exp $ + * $Id: ppbconf.h,v 1.9 1998/09/20 14:41:54 nsouch Exp $ * */ #ifndef __PPBCONF_H @@ -47,7 +47,12 @@ #define PPB_EPP 0x4 /* EPP mode, 32 bit */ #define PPB_ECP 0x8 /* ECP mode */ -#define PPB_SPP PPB_NIBBLE|PPB_PS2 +/* mode aliases */ +#define PPB_SPP PPB_NIBBLE|PPB_PS2 +#define PPB_BYTE PPB_PS2 + +#define PPB_MASK 0x0f +#define PPB_OPTIONS_MASK 0xf0 #define PPB_IS_EPP(mode) (mode & PPB_EPP) #define PPB_IN_EPP_MODE(dev) (PPB_IS_EPP (ppb_get_mode (dev))) @@ -103,6 +108,8 @@ struct ppb_status { #define PPB_NOINTR 0 #define PPB_WAIT 0x1 #define PPB_INTR 0x2 +#define PPB_POLL 0x4 +#define PPB_FOREVER -1 /* * Microsequence stuff. @@ -144,14 +151,14 @@ struct ppb_context { struct microseq *curmsq; /* currently executed microseqence */ }; - struct ppb_device { int id_unit; /* unit of the device */ char *name; /* name of the device */ ushort mode; /* current mode of the device */ - ushort avm; /* available modes of the device */ + ushort avm; /* available IEEE1284 modes of + * the device */ struct ppb_context ctx; /* context of the device */ @@ -184,6 +191,8 @@ struct ppb_adapter { int (*exec_microseq)(int, struct ppb_microseq **); int (*setmode)(int, int); + int (*read)(int, char *, int, int); + int (*write)(int, char *, int, int); void (*outsb_epp)(int, char *, int); void (*outsw_epp)(int, char *, int); @@ -230,7 +239,7 @@ struct ppb_link { /* * Maximum size of the PnP info string */ -#define PPB_PnP_STRING_SIZE 128 /* XXX */ +#define PPB_PnP_STRING_SIZE 256 /* XXX */ /* * Parallel Port Bus structure. @@ -250,10 +259,11 @@ struct ppb_data { #define PPB_PnP_UNKNOWN 10 int class_id; /* not a PnP device if class_id < 0 */ + int state; /* current IEEE1284 state */ + int error; /* last IEEE1284 error */ + ushort mode; /* IEEE 1284-1994 mode * NIBBLE, PS2, EPP or ECP */ - ushort avm; /* IEEE 1284-1994 available - * modes */ struct ppb_link *ppb_link; /* link to the adapter */ struct ppb_device *ppb_owner; /* device which owns the bus */ @@ -294,6 +304,7 @@ extern int ppb_ecp_sync(struct ppb_device *); extern int ppb_get_status(struct ppb_device *, struct ppb_status *); extern int ppb_set_mode(struct ppb_device *, int); +extern int ppb_write(struct ppb_device *, char *, int, int); /* * These are defined as macros for speedup. diff --git a/sys/dev/ppbus/ppi.c b/sys/dev/ppbus/ppi.c index 3103ee3..e0072f3 100644 --- a/sys/dev/ppbus/ppi.c +++ b/sys/dev/ppbus/ppi.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppi.c,v 1.7 1998/06/07 17:09:49 dfr Exp $ + * $Id: ppi.c,v 1.8 1998/12/07 21:58:16 archie Exp $ * */ #include "ppi.h" @@ -34,18 +34,35 @@ #include <sys/systm.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/uio.h> #include <sys/malloc.h> #include <sys/fcntl.h> +#include <machine/clock.h> + #include <dev/ppbus/ppbconf.h> +#include <dev/ppbus/ppb_msq.h> + +#include "opt_ppb_1284.h" + +#ifdef PERIPH_1284 +#include <dev/ppbus/ppb_1284.h> +#endif + #include <dev/ppbus/ppi.h> +#define BUFSIZE 512 struct ppi_data { int ppi_unit; int ppi_flags; #define HAVE_PPBUS (1<<0) +#define HAD_PPBUS (1<<1) + + int ppi_count; + int ppi_mode; /* IEEE1284 mode */ + char ppi_buffer[BUFSIZE]; struct ppb_device ppi_dev; }; @@ -70,13 +87,41 @@ DATA_SET(ppbdriver_set, ppidriver); static d_open_t ppiopen; static d_close_t ppiclose; static d_ioctl_t ppiioctl; +static d_write_t ppiwrite; +static d_read_t ppiread; #define CDEV_MAJOR 82 static struct cdevsw ppi_cdevsw = - { ppiopen, ppiclose, noread, nowrite, /* 82 */ + { ppiopen, ppiclose, ppiread, ppiwrite, /* 82 */ ppiioctl, nullstop, nullreset, nodevtotty, seltrue, nommap, nostrat, "ppi", NULL, -1 }; +#ifdef PERIPH_1284 + +static void +ppi_enable_intr(struct ppi_data *ppi) +{ + char r; + + r = ppb_rctr(&ppi->ppi_dev); + ppb_wctr(&ppi->ppi_dev, r | IRQENABLE); + + return; +} + +static void +ppi_disable_intr(struct ppi_data *ppi) +{ + char r; + + r = ppb_rctr(&ppi->ppi_dev); + ppb_wctr(&ppi->ppi_dev, r & ~IRQENABLE); + + return; +} + +#endif /* PERIPH_1284 */ + /* * ppiprobe() */ @@ -125,9 +170,72 @@ ppiattach(struct ppb_device *dev) return (1); } +/* + * Cable + * ----- + * + * Use an IEEE1284 compliant (DB25/DB25) cable with the following tricks: + * + * nStrobe <-> nAck 1 <-> 10 + * nAutofd <-> Busy 11 <-> 14 + * nSelectin <-> Select 17 <-> 13 + * nInit <-> nFault 15 <-> 16 + * + */ static void ppiintr(int unit) { +#ifdef PERIPH_1284 + struct ppi_data *ppi = ppidata[unit]; + + ppi_disable_intr(ppi); + + switch (ppi->ppi_dev.ppb->state) { + + /* accept IEEE1284 negociation then wakeup an waiting process to + * continue negociation at process level */ + case PPB_FORWARD_IDLE: + /* Event 1 */ + if ((ppb_rstr(&ppi->ppi_dev) & (SELECT | nBUSY)) == + (SELECT | nBUSY)) { + /* IEEE1284 negociation */ +#ifdef DEBUG_1284 + printf("N"); +#endif + + /* Event 2 - prepare for reading the ext. value */ + ppb_wctr(&ppi->ppi_dev, (PCD | STROBE | nINIT) & ~SELECTIN); + + ppi->ppi_dev.ppb->state = PPB_NEGOCIATION; + + } else { +#ifdef DEBUG_1284 + printf("0x%x", ppb_rstr(&ppi->ppi_dev)); +#endif + ppb_peripheral_terminate(&ppi->ppi_dev, PPB_DONTWAIT); + break; + } + + /* wake up any process waiting for negociation from + * remote master host */ + + /* XXX should set a variable to warn the process about + * the interrupt */ + + wakeup(ppi); + break; + default: +#ifdef DEBUG_1284 + printf("?%d", ppi->ppi_dev.ppb->state); +#endif + ppi->ppi_dev.ppb->state = PPB_FORWARD_IDLE; + ppb_set_mode(&ppi->ppi_dev, PPB_COMPATIBLE); + break; + } + + ppi_enable_intr(ppi); +#endif /* PERIPH_1284 */ + return; } @@ -141,11 +249,16 @@ ppiopen(dev_t dev, int flags, int fmt, struct proc *p) if (unit >= nppi) return (ENXIO); - if (!(ppi->ppi_flags & HAVE_PPBUS)) - if ((res = ppb_request_bus(&ppi->ppi_dev, (flags & O_NONBLOCK) ? PPB_DONTWAIT : (PPB_WAIT | PPB_INTR)))) + if (!(ppi->ppi_flags & HAVE_PPBUS)) { + if ((res = ppb_request_bus(&ppi->ppi_dev, + (flags & O_NONBLOCK) ? PPB_DONTWAIT : + (PPB_WAIT | PPB_INTR)))) return (res); - ppi->ppi_flags |= HAVE_PPBUS; + ppi->ppi_flags |= HAVE_PPBUS; + } + ppi->ppi_count += 1; + return (0); } @@ -155,12 +268,206 @@ ppiclose(dev_t dev, int flags, int fmt, struct proc *p) u_int unit = minor(dev); struct ppi_data *ppi = ppidata[unit]; - if (ppi->ppi_flags & HAVE_PPBUS) + ppi->ppi_count --; + if (!ppi->ppi_count) { + +#ifdef PERIPH_1284 + switch (ppi->ppi_dev.ppb->state) { + case PPB_PERIPHERAL_IDLE: + ppb_peripheral_terminate(&ppi->ppi_dev, 0); + break; + case PPB_REVERSE_IDLE: + case PPB_EPP_IDLE: + case PPB_ECP_FORWARD_IDLE: + default: + ppb_1284_terminate(&ppi->ppi_dev); + break; + } +#endif /* PERIPH_1284 */ + ppb_release_bus(&ppi->ppi_dev); - ppi->ppi_flags &= ~HAVE_PPBUS; + ppi->ppi_flags &= ~HAVE_PPBUS; + } + return (0); } +/* + * ppiread() + * + * IEEE1284 compliant read. + * + * First, try negociation to BYTE then NIBBLE mode + * If no data is available, wait for it otherwise transfer as much as possible + */ +static int +ppiread(dev_t dev, struct uio *uio, int ioflag) +{ +#ifdef PERIPH_1284 + u_int unit = minor(dev); + struct ppi_data *ppi = ppidata[unit]; + int len, error = 0; + + switch (ppi->ppi_dev.ppb->state) { + case PPB_PERIPHERAL_IDLE: + ppb_peripheral_terminate(&ppi->ppi_dev, 0); + /* fall throught */ + + case PPB_FORWARD_IDLE: + /* if can't negociate NIBBLE mode then try BYTE mode, + * the peripheral may be a computer + */ + if ((ppb_1284_negociate(&ppi->ppi_dev, + ppi->ppi_mode = PPB_NIBBLE, 0))) { + + /* XXX Wait 2 seconds to let the remote host some + * time to terminate its interrupt + */ + tsleep(ppi, PPBPRI, "ppiread", 2*hz); + + if ((error = ppb_1284_negociate(&ppi->ppi_dev, + ppi->ppi_mode = PPB_BYTE, 0))) + return (error); + } + break; + + case PPB_REVERSE_IDLE: + case PPB_EPP_IDLE: + case PPB_ECP_FORWARD_IDLE: + default: + break; + } + +#ifdef DEBUG_1284 + printf("N"); +#endif + /* read data */ + len = 0; + while (uio->uio_resid) { + if ((error = ppb_1284_read(&ppi->ppi_dev, ppi->ppi_mode, + ppi->ppi_buffer, min(BUFSIZE, uio->uio_resid), + &len))) { + goto error; + } + + if (!len) + goto error; /* no more data */ + +#ifdef DEBUG_1284 + printf("d"); +#endif + if ((error = uiomove(ppi->ppi_buffer, len, uio))) + goto error; + } + +error: + +#else /* PERIPH_1284 */ + int error = ENODEV; +#endif + + return (error); +} + +/* + * ppiwrite() + * + * IEEE1284 compliant write + * + * Actually, this is the peripheral side of a remote IEEE1284 read + * + * The first part of the negociation (IEEE1284 device detection) is + * done at interrupt level, then the remaining is done by the writing + * process + * + * Once negociation done, transfer data + */ +static int +ppiwrite(dev_t dev, struct uio *uio, int ioflag) +{ +#ifdef PERIPH_1284 + u_int unit = minor(dev); + struct ppi_data *ppi = ppidata[unit]; + struct ppb_data *ppb = ppi->ppi_dev.ppb; + int len, error = 0, sent; + +#if 0 + int ret; + + #define ADDRESS MS_PARAM(0, 0, MS_TYP_PTR) + #define LENGTH MS_PARAM(0, 1, MS_TYP_INT) + + struct ppb_microseq msq[] = { + { MS_OP_PUT, { MS_UNKNOWN, MS_UNKNOWN, MS_UNKNOWN } }, + MS_RET(0) + }; + + /* negociate ECP mode */ + if (ppb_1284_negociate(&ppi->ppi_dev, PPB_ECP, 0)) { + printf("ppiwrite: ECP negociation failed\n"); + } + + while (!error && (len = min(uio->uio_resid, BUFSIZE))) { + uiomove(ppi->ppi_buffer, len, uio); + + ppb_MS_init_msq(msq, 2, ADDRESS, ppi->ppi_buffer, LENGTH, len); + + error = ppb_MS_microseq(&ppi->ppi_dev, msq, &ret); + } +#endif + + /* we have to be peripheral to be able to send data, so + * wait for the appropriate state + */ + if (ppb->state < PPB_PERIPHERAL_NEGOCIATION) + ppb_1284_terminate(&ppi->ppi_dev); + + while (ppb->state != PPB_PERIPHERAL_IDLE) { + /* XXX should check a variable before sleeping */ +#ifdef DEBUG_1284 + printf("s"); +#endif + + ppi_enable_intr(ppi); + + /* sleep until IEEE1284 negociation starts */ + error = tsleep(ppi, PCATCH | PPBPRI, "ppiwrite", 0); + + switch (error) { + case 0: + /* negociate peripheral side with BYTE mode */ + ppb_peripheral_negociate(&ppi->ppi_dev, PPB_BYTE, 0); + break; + case EWOULDBLOCK: + break; + default: + goto error; + } + } +#ifdef DEBUG_1284 + printf("N"); +#endif + + /* negociation done, write bytes to master host */ + while (len = min(uio->uio_resid, BUFSIZE)) { + uiomove(ppi->ppi_buffer, len, uio); + if ((error = byte_peripheral_write(&ppi->ppi_dev, + ppi->ppi_buffer, len, &sent))) + goto error; +#ifdef DEBUG_1284 + printf("d"); +#endif + } + +error: + +#else /* PERIPH_1284 */ + int error = ENODEV; +#endif + + return (error); +} + static int ppiioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) { @@ -208,7 +515,6 @@ ppiioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) case PPISFIFO: /* write FIFO */ ppb_wfifo(&ppi->ppi_dev, *val); break; - default: error = ENOTTY; break; diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c index 82e9dab..17b67c5 100644 --- a/sys/dev/ppbus/vpo.c +++ b/sys/dev/ppbus/vpo.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: vpo.c,v 1.9 1998/12/07 21:58:16 archie Exp $ + * $Id: vpo.c,v 1.10 1999/01/09 18:05:46 nsouch Exp $ * */ @@ -51,6 +51,8 @@ #include <sys/kernel.h> #endif /*KERNEL */ +#include "opt_vpo.h" + #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/vpoio.h> diff --git a/sys/dev/ppbus/vpoio.c b/sys/dev/ppbus/vpoio.c index e42eca7..54791ee 100644 --- a/sys/dev/ppbus/vpoio.c +++ b/sys/dev/ppbus/vpoio.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: vpoio.c,v 1.3 1998/09/20 14:41:54 nsouch Exp $ + * $Id: vpoio.c,v 1.4 1998/12/07 21:58:16 archie Exp $ * */ @@ -41,6 +41,8 @@ #include <sys/kernel.h> #endif /*KERNEL */ +#include "opt_vpo.h" + #include <dev/ppbus/ppbconf.h> #include <dev/ppbus/ppb_msq.h> #include <dev/ppbus/vpoio.h> @@ -284,8 +286,13 @@ vpoio_connect(struct vpoio_data *vpo, int how) int error; int ret; - if ((error = ppb_request_bus(&vpo->vpo_dev, how))) + if ((error = ppb_request_bus(&vpo->vpo_dev, how))) { + +#ifdef VP0_DEBUG + printf("%s: can't request bus!\n", __FUNCTION__); +#endif return error; + } if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret); @@ -437,8 +444,8 @@ vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); } - /* ppb_ecp_sync(&vpo->vpo_dev); */ #endif + ppb_ecp_sync(&vpo->vpo_dev); return (error); } @@ -476,8 +483,8 @@ vpoio_instr(struct vpoio_data *vpo, char *buffer, int size) ppb_wctr(&vpo->vpo_dev, PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE); } - /* ppb_ecp_sync(&vpo->vpo_dev); */ #endif + ppb_ecp_sync(&vpo->vpo_dev); return (error); } @@ -701,6 +708,10 @@ vpoio_reset_bus(struct vpoio_data *vpo) { /* first, connect to the drive */ if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { + +#ifdef VP0_DEBUG + printf("%s: not in disk mode!\n", __FUNCTION__); +#endif /* release ppbus */ vpoio_disconnect(vpo); return (1); diff --git a/sys/dev/ppc/ppc.c b/sys/dev/ppc/ppc.c index e351688..d2bc7a1 100644 --- a/sys/dev/ppc/ppc.c +++ b/sys/dev/ppc/ppc.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppc.c,v 1.12 1998/12/07 21:58:22 archie Exp $ + * $Id: ppc.c,v 1.13 1998/12/30 00:37:42 hoek Exp $ * */ #include "ppc.h" @@ -49,6 +49,11 @@ #include <i386/isa/ppcreg.h> +#include "opt_ppc.h" + +#define LOG_PPC(function, ppc, string) \ + if (bootverbose) printf("%s: %s\n", function, string) + static int ppcprobe(struct isa_device *); static int ppcattach(struct isa_device *); @@ -130,6 +135,9 @@ static int ppc_exec_microseq(int, struct ppb_microseq **); static int ppc_generic_setmode(int, int); static int ppc_smclike_setmode(int, int); +static int ppc_read(int, char *, int, int); +static int ppc_write(int, char *, int, int); + static struct ppb_adapter ppc_smclike_adapter = { 0, /* no intr handler, filled by chipset dependent code */ @@ -138,7 +146,7 @@ static struct ppb_adapter ppc_smclike_adapter = { ppc_exec_microseq, - ppc_smclike_setmode, + ppc_smclike_setmode, ppc_read, ppc_write, ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -155,7 +163,7 @@ static struct ppb_adapter ppc_generic_adapter = { ppc_exec_microseq, - ppc_generic_setmode, + ppc_generic_setmode, ppc_read, ppc_write, ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -173,8 +181,11 @@ ppc_ecp_sync(int unit) { struct ppc_data *ppc = ppcdata[unit]; int i, r; + if (!(ppc->ppc_avm & PPB_ECP)) + return; + r = r_ecr(ppc); - if ((r & 0xe0) != 0x80) + if ((r & 0xe0) != PPC_ECR_EPP) return; for (i = 0; i < 100; i++) { @@ -190,13 +201,115 @@ ppc_ecp_sync(int unit) { return; } -static void -ppcintr(int unit) +/* + * ppc_detect_fifo() + * + * Detect parallel port FIFO + */ +static int +ppc_detect_fifo(struct ppc_data *ppc) { - /* call directly upper code */ - ppb_intr(&ppcdata[unit]->ppc_link); + char ecr_sav; + char ctr_sav, ctr, cc; + short i; + + /* save registers */ + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); - return; + /* enter ECP configuration mode, no interrupt, no DMA */ + w_ecr(ppc, 0xf4); + + /* read PWord size - transfers in FIFO mode must be PWord aligned */ + ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK); + + /* XXX 16 and 32 bits implementations not supported */ + if (ppc->ppc_pword != PPC_PWORD_8) { + LOG_PPC(__FUNCTION__, ppc, "PWord not supported"); + goto error; + } + + w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */ + ctr = r_ctr(ppc); + w_ctr(ppc, ctr | PCD); /* set direction to 1 */ + + /* enter ECP test mode, no interrupt, no DMA */ + w_ecr(ppc, 0xd4); + + /* flush the FIFO */ + for (i=0; i<1024; i++) { + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + break; + cc = r_fifo(ppc); + } + + if (i >= 1024) { + LOG_PPC(__FUNCTION__, ppc, "can't flush FIFO"); + goto error; + } + + /* enable interrupts, no DMA */ + w_ecr(ppc, 0xd0); + + /* determine readIntrThreshold + * fill the FIFO until serviceIntr is set + */ + for (i=0; i<1024; i++) { + w_fifo(ppc, (char)i); + if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) { + /* readThreshold reached */ + ppc->ppc_rthr = i+1; + } + if (r_ecr(ppc) & PPC_FIFO_FULL) { + ppc->ppc_fifo = i+1; + break; + } + } + + if (i >= 1024) { + LOG_PPC(__FUNCTION__, ppc, "can't fill FIFO"); + goto error; + } + + w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */ + w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */ + w_ecr(ppc, 0xd0); /* enable interrupts */ + + /* determine writeIntrThreshold + * empty the FIFO until serviceIntr is set + */ + for (i=ppc->ppc_fifo; i>0; i--) { + if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) { + LOG_PPC(__FUNCTION__, ppc, "invalid data in FIFO"); + goto error; + } + if (r_ecr(ppc) & PPC_SERVICE_INTR) { + /* writeIntrThreshold reached */ + ppc->ppc_wthr = ppc->ppc_fifo - i+1; + } + /* if FIFO empty before the last byte, error */ + if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) { + LOG_PPC(__FUNCTION__, ppc, "data lost in FIFO"); + goto error; + } + } + + /* FIFO must be empty after the last byte */ + if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + LOG_PPC(__FUNCTION__, ppc, "can't empty the FIFO"); + goto error; + } + + w_ctr(ppc, ctr_sav); + w_ecr(ppc, ecr_sav); + + return (0); + +error: + w_ctr(ppc, ctr_sav); + w_ecr(ppc, ecr_sav); + + return (EINVAL); } static int @@ -654,6 +767,13 @@ config: ppc->ppc_avm = chipset_mode; } + /* set FIFO threshold to 16 */ + if (ppc->ppc_avm & PPB_ECP) { + /* select CRA */ + outb(csr, 0xa); + outb(cio, 16); + } + end_detect: if (bootverbose) @@ -884,14 +1004,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) if (!chipset_mode) { /* first, check for ECP */ - w_ecr(ppc, 0x20); - if ((r_ecr(ppc) & 0xe0) == 0x20) { + w_ecr(ppc, PPC_ECR_PS2); + if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) { ppc->ppc_avm |= PPB_ECP | PPB_SPP; if (bootverbose) printf(" ECP SPP"); /* search for SMC style ECP+EPP mode */ - w_ecr(ppc, 0x80); + w_ecr(ppc, PPC_ECR_EPP); } /* try to reset EPP timeout bit */ @@ -911,7 +1031,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) } } else { /* restore to standard mode */ - w_ecr(ppc, 0x0); + w_ecr(ppc, PPC_ECR_STD); } /* XXX try to detect NIBBLE and PS2 modes */ @@ -978,6 +1098,10 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { } } + /* configure/detect ECP FIFO */ + if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80)) + ppc_detect_fifo(ppc); + return (0); } @@ -1202,6 +1326,244 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq) /* unreached */ } +static void +ppcintr(int unit) +{ + struct ppc_data *ppc = ppcdata[unit]; + char ctr, ecr; + + ctr = r_ctr(ppc); + ecr = r_ecr(ppc); + +#ifdef PPC_DEBUG + printf("!"); +#endif + + /* don't use ecp mode with IRQENABLE set */ + if (ctr & IRQENABLE) { + /* call upper code */ + ppb_intr(&ppc->ppc_link); + return; + } + + if (ctr & nFAULT) { + if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) { + + w_ecr(ppc, ecr | PPC_nFAULT_INTR); + ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT; + } else { + /* call upper code */ + ppb_intr(&ppc->ppc_link); + return; + } + } + + if (ppc->ppc_irqstat & PPC_IRQ_DMA) { + /* disable interrupts (should be done by hardware though) */ + w_ecr(ppc, ecr | PPC_SERVICE_INTR); + ppc->ppc_irqstat &= ~PPC_IRQ_DMA; + ecr = r_ecr(ppc); + + /* check if DMA completed */ + if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) { +#ifdef PPC_DEBUG + printf("a"); +#endif + /* stop DMA */ + w_ecr(ppc, ecr & ~PPC_ENABLE_DMA); + ecr = r_ecr(ppc); + + if (ppc->ppc_dmastat == PPC_DMA_STARTED) { +#ifdef PPC_DEBUG + printf("d"); +#endif + isa_dmadone( + ppc->ppc_dmaflags, + ppc->ppc_dmaddr, + ppc->ppc_dmacnt, + ppc->ppc_dmachan); + + ppc->ppc_dmastat = PPC_DMA_COMPLETE; + + /* wakeup the waiting process */ + wakeup((caddr_t)ppc); + } + } + } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) { + + /* classic interrupt I/O */ + ppc->ppc_irqstat &= ~PPC_IRQ_FIFO; + + } + + return; +} + +static int +ppc_read(int unit, char *buf, int len, int mode) +{ + return (EINVAL); +} + +/* + * Call this function if you want to send data in any advanced mode + * of your parallel port: FIFO, DMA + * + * If what you want is not possible (no ECP, no DMA...), + * EINVAL is returned + */ +static int +ppc_write(int unit, char *buf, int len, int how) +{ + struct ppc_data *ppc = ppcdata[unit]; + char ecr, ecr_sav, ctr, ctr_sav; + int s, error = 0; + int spin; + +#ifdef PPC_DEBUG + printf("w"); +#endif + + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); + + /* + * Send buffer with DMA, FIFO and interrupts + */ + if (ppc->ppc_avm & PPB_ECP) { + + if (ppc->ppc_dmachan >= 0) { + + /* byte mode, no intr, no DMA, dir=0, flush fifo + */ + ecr = PPC_ECR_STD | PPC_DISABLE_INTR; + w_ecr(ppc, ecr); + + /* disable nAck interrupts */ + ctr = r_ctr(ppc); + ctr &= ~IRQENABLE; + w_ctr(ppc, ctr); + + ppc->ppc_dmaflags = 0; + ppc->ppc_dmaddr = (caddr_t)buf; + ppc->ppc_dmacnt = (u_int)len; + + switch (ppc->ppc_mode) { + case PPB_COMPATIBLE: + /* compatible mode with FIFO, no intr, DMA, dir=0 */ + ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + case PPB_ECP: + ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + default: + error = EINVAL; + goto error; + } + + w_ecr(ppc, ecr); + ecr = r_ecr(ppc); + + /* enter splhigh() not to be preempted + * by the dma interrupt, we may miss + * the wakeup otherwise + */ + s = splhigh(); + + ppc->ppc_dmastat = PPC_DMA_INIT; + + /* enable interrupts */ + ecr &= ~PPC_SERVICE_INTR; + ppc->ppc_irqstat = PPC_IRQ_DMA; + w_ecr(ppc, ecr); + + isa_dmastart( + ppc->ppc_dmaflags, + ppc->ppc_dmaddr, + ppc->ppc_dmacnt, + ppc->ppc_dmachan); +#ifdef PPC_DEBUG + printf("s%d", ppc->ppc_dmacnt); +#endif + ppc->ppc_dmastat = PPC_DMA_STARTED; + + /* Wait for the DMA completed interrupt. We hope we won't + * miss it, otherwise a signal will be necessary to unlock the + * process. + */ + do { + /* release CPU */ + error = tsleep((caddr_t)ppc, + PPBPRI | PCATCH, "ppcdma", 0); + + } while (error == EWOULDBLOCK); + + splx(s); + + if (error) { +#ifdef PPC_DEBUG + printf("i"); +#endif + /* stop DMA */ + isa_dmadone( + ppc->ppc_dmaflags, ppc->ppc_dmaddr, + ppc->ppc_dmacnt, ppc->ppc_dmachan); + + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + goto error; + } + + /* wait for an empty fifo */ + while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + + for (spin=100; spin; spin--) + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + goto fifo_empty; +#ifdef PPC_DEBUG + printf("Z"); +#endif + error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100); + if (error != EWOULDBLOCK) { +#ifdef PPC_DEBUG + printf("I"); +#endif + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + error = EINTR; + goto error; + } + } + +fifo_empty: + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + } else + error = EINVAL; /* XXX we should FIFO and + * interrupts */ + } else + error = EINVAL; + +error: + + /* PDRQ must be kept unasserted until nPDACK is + * deasserted for a minimum of 350ns (SMC datasheet) + * + * Consequence may be a FIFO that never empty + */ + DELAY(1); + + w_ecr(ppc, ecr_sav); + w_ctr(ppc, ctr_sav); + + return (error); +} + /* * Configure current operating mode */ @@ -1209,32 +1571,34 @@ static int ppc_generic_setmode(int unit, int mode) { struct ppc_data *ppc = ppcdata[unit]; - - /* back to compatible mode, XXX don't know yet what to do here */ - if (mode == 0) { - ppc->ppc_mode = PPB_COMPATIBLE; - return (0); - } + u_char ecr = 0; /* check if mode is available */ - if (!(ppc->ppc_avm & mode)) - return (EOPNOTSUPP); + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); /* if ECP mode, configure ecr register */ if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; - /* XXX disable DMA, enable interrupts */ if (mode & PPB_EPP) - return (EOPNOTSUPP); - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - w_ecr(ppc, 0x20); + return (EINVAL); else if (mode & PPB_ECP) /* select ECP mode */ - w_ecr(ppc, 0x60); + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; else - /* select standard parallel port mode */ - w_ecr(ppc, 0x00); + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); } ppc->ppc_mode = mode; @@ -1242,42 +1606,50 @@ ppc_generic_setmode(int unit, int mode) return (0); } +/* + * The ppc driver is free to choose options like FIFO or DMA + * if ECP mode is available. + * + * The 'RAW' option allows the upper drivers to force the ppc mode + * even with FIFO, DMA available. + */ int ppc_smclike_setmode(int unit, int mode) { struct ppc_data *ppc = ppcdata[unit]; - - /* back to compatible mode, XXX don't know yet what to do here */ - if (mode == 0) { - ppc->ppc_mode = PPB_COMPATIBLE; - return (0); - } + u_char ecr = 0; /* check if mode is available */ - if (!(ppc->ppc_avm & mode)) - return (EOPNOTSUPP); + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); /* if ECP mode, configure ecr register */ if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP or EPP mode + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; - /* XXX disable DMA, enable interrupts */ if (mode & PPB_EPP) /* select EPP mode */ - w_ecr(ppc, 0x80); - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - w_ecr(ppc, 0x20); + ecr |= PPC_ECR_EPP; else if (mode & PPB_ECP) /* select ECP mode */ - w_ecr(ppc, 0x60); + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; else - /* select standard parallel port mode */ - w_ecr(ppc, 0x00); + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); } ppc->ppc_mode = mode; - return (0); } @@ -1314,8 +1686,9 @@ ppcprobe(struct isa_device *dvp) if((next_bios_ppc < BIOS_MAX_PPC) && (*(BIOS_PORTS+next_bios_ppc) != 0) ) { dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++); - printf("ppc: parallel port found at 0x%x\n", - dvp->id_iobase); + if (bootverbose) + printf("ppc: parallel port found at 0x%x\n", + dvp->id_iobase); } else return (0); } @@ -1351,6 +1724,8 @@ ppcprobe(struct isa_device *dvp) if (!(dvp->id_flags & 0x20)) ppc->ppc_irq = ffs(dvp->id_irq) - 1; + ppc->ppc_dmachan = dvp->id_drq; + ppcdata[ppc->ppc_unit] = ppc; nppc ++; @@ -1384,6 +1759,11 @@ ppcattach(struct isa_device *isdp) ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? ppc_epp_protocol[ppc->ppc_epp] : ""); + if (ppc->ppc_fifo) + printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n", + ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr, + ppc->ppc_rthr); + isdp->id_ointr = ppcintr; /* @@ -1397,6 +1777,13 @@ ppcattach(struct isa_device *isdp) ppc->ppc_link.ppbus = ppbus; ppbus->ppb_link = &ppc->ppc_link; + if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { + + /* acquire the DMA channel forever */ + isa_dma_acquire(ppc->ppc_dmachan); + isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ + } + /* * Probe the ppbus and attach devices found. */ diff --git a/sys/dev/ppc/ppcreg.h b/sys/dev/ppc/ppcreg.h index af8c9db..829bfd1 100644 --- a/sys/dev/ppc/ppcreg.h +++ b/sys/dev/ppc/ppcreg.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppcreg.h,v 1.4 1998/09/13 18:26:44 nsouch Exp $ + * $Id: ppcreg.h,v 1.5 1998/10/31 11:37:09 nsouch Exp $ * */ #ifndef __PPCREG_H @@ -55,6 +55,34 @@ struct ppc_data { int ppc_mode; /* chipset current mode */ int ppc_avm; /* chipset available modes */ +#define PPC_IRQ_NONE 0x0 +#define PPC_IRQ_nACK 0x1 +#define PPC_IRQ_DMA 0x2 +#define PPC_IRQ_FIFO 0x4 +#define PPC_IRQ_nFAULT 0x8 + int ppc_irqstat; /* remind irq settings */ + +#define PPC_DMA_INIT 0x01 +#define PPC_DMA_STARTED 0x02 +#define PPC_DMA_COMPLETE 0x03 +#define PPC_DMA_INTERRUPTED 0x04 +#define PPC_DMA_ERROR 0x05 + int ppc_dmastat; /* dma state */ + int ppc_dmachan; /* dma channel */ + int ppc_dmaflags; /* dma transfer flags */ + caddr_t ppc_dmaddr; /* buffer address */ + u_int ppc_dmacnt; /* count of bytes sent with dma */ + +#define PPC_PWORD_MASK 0x30 +#define PPC_PWORD_16 0x00 +#define PPC_PWORD_8 0x10 +#define PPC_PWORD_32 0x20 + char ppc_pword; /* PWord size */ + short ppc_fifo; /* FIFO threshold */ + + short ppc_wthr; /* writeIntrThresold */ + short ppc_rthr; /* readIntrThresold */ + #define ppc_base ppc_link.base #define ppc_epp ppc_link.epp_protocol #define ppc_irq ppc_link.id_irq @@ -71,25 +99,44 @@ struct ppc_data { * Parallel Port Chipset registers. */ #define PPC_SPP_DTR 0 /* SPP data register */ +#define PPC_ECP_A_FIFO 0 /* ECP Address fifo register */ #define PPC_SPP_STR 1 /* SPP status register */ #define PPC_SPP_CTR 2 /* SPP control register */ #define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */ -#define PPC_ECP_FIFO 0x400 /* ECP fifo register */ +#define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */ +#define PPC_ECP_CNFGA 0x400 /* Configuration register A */ +#define PPC_ECP_CNFGB 0x401 /* Configuration register B */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ +#define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */ +#define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */ +#define PPC_SERVICE_INTR 0x4 /* ecr register - bit 2 */ +#define PPC_ENABLE_DMA 0x8 /* ecr register - bit 3 */ +#define PPC_nFAULT_INTR 0x10 /* ecr register - bit 4 */ +#define PPC_ECR_STD 0x0 +#define PPC_ECR_PS2 0x20 +#define PPC_ECR_FIFO 0x40 +#define PPC_ECR_ECP 0x60 +#define PPC_ECR_EPP 0x80 + +#define PPC_DISABLE_INTR (PPC_SERVICE_INTR | PPC_nFAULT_INTR) +#define PPC_ECR_RESET (PPC_ECR_PS2 | PPC_DISABLE_INTR) + #define r_dtr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_DTR)) #define r_str(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_STR)) #define r_ctr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_CTR)) #define r_epp(ppc) ((char)inb((ppc)->ppc_base + PPC_EPP_DATA)) +#define r_cnfgA(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGA)) +#define r_cnfgB(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGB)) #define r_ecr(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_ECR)) -#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_FIFO)) +#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_D_FIFO)) #define w_dtr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_DTR, byte) #define w_str(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_STR, byte) #define w_ctr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_CTR, byte) #define w_epp(ppc,byte) outb((ppc)->ppc_base + PPC_EPP_DATA, byte) #define w_ecr(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_ECR, byte) -#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_FIFO, byte) +#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_D_FIFO, byte) /* * Register defines for the PC873xx parts diff --git a/sys/i386/isa/ppc.c b/sys/i386/isa/ppc.c index e351688..d2bc7a1 100644 --- a/sys/i386/isa/ppc.c +++ b/sys/i386/isa/ppc.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppc.c,v 1.12 1998/12/07 21:58:22 archie Exp $ + * $Id: ppc.c,v 1.13 1998/12/30 00:37:42 hoek Exp $ * */ #include "ppc.h" @@ -49,6 +49,11 @@ #include <i386/isa/ppcreg.h> +#include "opt_ppc.h" + +#define LOG_PPC(function, ppc, string) \ + if (bootverbose) printf("%s: %s\n", function, string) + static int ppcprobe(struct isa_device *); static int ppcattach(struct isa_device *); @@ -130,6 +135,9 @@ static int ppc_exec_microseq(int, struct ppb_microseq **); static int ppc_generic_setmode(int, int); static int ppc_smclike_setmode(int, int); +static int ppc_read(int, char *, int, int); +static int ppc_write(int, char *, int, int); + static struct ppb_adapter ppc_smclike_adapter = { 0, /* no intr handler, filled by chipset dependent code */ @@ -138,7 +146,7 @@ static struct ppb_adapter ppc_smclike_adapter = { ppc_exec_microseq, - ppc_smclike_setmode, + ppc_smclike_setmode, ppc_read, ppc_write, ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -155,7 +163,7 @@ static struct ppb_adapter ppc_generic_adapter = { ppc_exec_microseq, - ppc_generic_setmode, + ppc_generic_setmode, ppc_read, ppc_write, ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -173,8 +181,11 @@ ppc_ecp_sync(int unit) { struct ppc_data *ppc = ppcdata[unit]; int i, r; + if (!(ppc->ppc_avm & PPB_ECP)) + return; + r = r_ecr(ppc); - if ((r & 0xe0) != 0x80) + if ((r & 0xe0) != PPC_ECR_EPP) return; for (i = 0; i < 100; i++) { @@ -190,13 +201,115 @@ ppc_ecp_sync(int unit) { return; } -static void -ppcintr(int unit) +/* + * ppc_detect_fifo() + * + * Detect parallel port FIFO + */ +static int +ppc_detect_fifo(struct ppc_data *ppc) { - /* call directly upper code */ - ppb_intr(&ppcdata[unit]->ppc_link); + char ecr_sav; + char ctr_sav, ctr, cc; + short i; + + /* save registers */ + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); - return; + /* enter ECP configuration mode, no interrupt, no DMA */ + w_ecr(ppc, 0xf4); + + /* read PWord size - transfers in FIFO mode must be PWord aligned */ + ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK); + + /* XXX 16 and 32 bits implementations not supported */ + if (ppc->ppc_pword != PPC_PWORD_8) { + LOG_PPC(__FUNCTION__, ppc, "PWord not supported"); + goto error; + } + + w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */ + ctr = r_ctr(ppc); + w_ctr(ppc, ctr | PCD); /* set direction to 1 */ + + /* enter ECP test mode, no interrupt, no DMA */ + w_ecr(ppc, 0xd4); + + /* flush the FIFO */ + for (i=0; i<1024; i++) { + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + break; + cc = r_fifo(ppc); + } + + if (i >= 1024) { + LOG_PPC(__FUNCTION__, ppc, "can't flush FIFO"); + goto error; + } + + /* enable interrupts, no DMA */ + w_ecr(ppc, 0xd0); + + /* determine readIntrThreshold + * fill the FIFO until serviceIntr is set + */ + for (i=0; i<1024; i++) { + w_fifo(ppc, (char)i); + if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) { + /* readThreshold reached */ + ppc->ppc_rthr = i+1; + } + if (r_ecr(ppc) & PPC_FIFO_FULL) { + ppc->ppc_fifo = i+1; + break; + } + } + + if (i >= 1024) { + LOG_PPC(__FUNCTION__, ppc, "can't fill FIFO"); + goto error; + } + + w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */ + w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */ + w_ecr(ppc, 0xd0); /* enable interrupts */ + + /* determine writeIntrThreshold + * empty the FIFO until serviceIntr is set + */ + for (i=ppc->ppc_fifo; i>0; i--) { + if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) { + LOG_PPC(__FUNCTION__, ppc, "invalid data in FIFO"); + goto error; + } + if (r_ecr(ppc) & PPC_SERVICE_INTR) { + /* writeIntrThreshold reached */ + ppc->ppc_wthr = ppc->ppc_fifo - i+1; + } + /* if FIFO empty before the last byte, error */ + if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) { + LOG_PPC(__FUNCTION__, ppc, "data lost in FIFO"); + goto error; + } + } + + /* FIFO must be empty after the last byte */ + if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + LOG_PPC(__FUNCTION__, ppc, "can't empty the FIFO"); + goto error; + } + + w_ctr(ppc, ctr_sav); + w_ecr(ppc, ecr_sav); + + return (0); + +error: + w_ctr(ppc, ctr_sav); + w_ecr(ppc, ecr_sav); + + return (EINVAL); } static int @@ -654,6 +767,13 @@ config: ppc->ppc_avm = chipset_mode; } + /* set FIFO threshold to 16 */ + if (ppc->ppc_avm & PPB_ECP) { + /* select CRA */ + outb(csr, 0xa); + outb(cio, 16); + } + end_detect: if (bootverbose) @@ -884,14 +1004,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) if (!chipset_mode) { /* first, check for ECP */ - w_ecr(ppc, 0x20); - if ((r_ecr(ppc) & 0xe0) == 0x20) { + w_ecr(ppc, PPC_ECR_PS2); + if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) { ppc->ppc_avm |= PPB_ECP | PPB_SPP; if (bootverbose) printf(" ECP SPP"); /* search for SMC style ECP+EPP mode */ - w_ecr(ppc, 0x80); + w_ecr(ppc, PPC_ECR_EPP); } /* try to reset EPP timeout bit */ @@ -911,7 +1031,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) } } else { /* restore to standard mode */ - w_ecr(ppc, 0x0); + w_ecr(ppc, PPC_ECR_STD); } /* XXX try to detect NIBBLE and PS2 modes */ @@ -978,6 +1098,10 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { } } + /* configure/detect ECP FIFO */ + if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80)) + ppc_detect_fifo(ppc); + return (0); } @@ -1202,6 +1326,244 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq) /* unreached */ } +static void +ppcintr(int unit) +{ + struct ppc_data *ppc = ppcdata[unit]; + char ctr, ecr; + + ctr = r_ctr(ppc); + ecr = r_ecr(ppc); + +#ifdef PPC_DEBUG + printf("!"); +#endif + + /* don't use ecp mode with IRQENABLE set */ + if (ctr & IRQENABLE) { + /* call upper code */ + ppb_intr(&ppc->ppc_link); + return; + } + + if (ctr & nFAULT) { + if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) { + + w_ecr(ppc, ecr | PPC_nFAULT_INTR); + ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT; + } else { + /* call upper code */ + ppb_intr(&ppc->ppc_link); + return; + } + } + + if (ppc->ppc_irqstat & PPC_IRQ_DMA) { + /* disable interrupts (should be done by hardware though) */ + w_ecr(ppc, ecr | PPC_SERVICE_INTR); + ppc->ppc_irqstat &= ~PPC_IRQ_DMA; + ecr = r_ecr(ppc); + + /* check if DMA completed */ + if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) { +#ifdef PPC_DEBUG + printf("a"); +#endif + /* stop DMA */ + w_ecr(ppc, ecr & ~PPC_ENABLE_DMA); + ecr = r_ecr(ppc); + + if (ppc->ppc_dmastat == PPC_DMA_STARTED) { +#ifdef PPC_DEBUG + printf("d"); +#endif + isa_dmadone( + ppc->ppc_dmaflags, + ppc->ppc_dmaddr, + ppc->ppc_dmacnt, + ppc->ppc_dmachan); + + ppc->ppc_dmastat = PPC_DMA_COMPLETE; + + /* wakeup the waiting process */ + wakeup((caddr_t)ppc); + } + } + } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) { + + /* classic interrupt I/O */ + ppc->ppc_irqstat &= ~PPC_IRQ_FIFO; + + } + + return; +} + +static int +ppc_read(int unit, char *buf, int len, int mode) +{ + return (EINVAL); +} + +/* + * Call this function if you want to send data in any advanced mode + * of your parallel port: FIFO, DMA + * + * If what you want is not possible (no ECP, no DMA...), + * EINVAL is returned + */ +static int +ppc_write(int unit, char *buf, int len, int how) +{ + struct ppc_data *ppc = ppcdata[unit]; + char ecr, ecr_sav, ctr, ctr_sav; + int s, error = 0; + int spin; + +#ifdef PPC_DEBUG + printf("w"); +#endif + + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); + + /* + * Send buffer with DMA, FIFO and interrupts + */ + if (ppc->ppc_avm & PPB_ECP) { + + if (ppc->ppc_dmachan >= 0) { + + /* byte mode, no intr, no DMA, dir=0, flush fifo + */ + ecr = PPC_ECR_STD | PPC_DISABLE_INTR; + w_ecr(ppc, ecr); + + /* disable nAck interrupts */ + ctr = r_ctr(ppc); + ctr &= ~IRQENABLE; + w_ctr(ppc, ctr); + + ppc->ppc_dmaflags = 0; + ppc->ppc_dmaddr = (caddr_t)buf; + ppc->ppc_dmacnt = (u_int)len; + + switch (ppc->ppc_mode) { + case PPB_COMPATIBLE: + /* compatible mode with FIFO, no intr, DMA, dir=0 */ + ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + case PPB_ECP: + ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + default: + error = EINVAL; + goto error; + } + + w_ecr(ppc, ecr); + ecr = r_ecr(ppc); + + /* enter splhigh() not to be preempted + * by the dma interrupt, we may miss + * the wakeup otherwise + */ + s = splhigh(); + + ppc->ppc_dmastat = PPC_DMA_INIT; + + /* enable interrupts */ + ecr &= ~PPC_SERVICE_INTR; + ppc->ppc_irqstat = PPC_IRQ_DMA; + w_ecr(ppc, ecr); + + isa_dmastart( + ppc->ppc_dmaflags, + ppc->ppc_dmaddr, + ppc->ppc_dmacnt, + ppc->ppc_dmachan); +#ifdef PPC_DEBUG + printf("s%d", ppc->ppc_dmacnt); +#endif + ppc->ppc_dmastat = PPC_DMA_STARTED; + + /* Wait for the DMA completed interrupt. We hope we won't + * miss it, otherwise a signal will be necessary to unlock the + * process. + */ + do { + /* release CPU */ + error = tsleep((caddr_t)ppc, + PPBPRI | PCATCH, "ppcdma", 0); + + } while (error == EWOULDBLOCK); + + splx(s); + + if (error) { +#ifdef PPC_DEBUG + printf("i"); +#endif + /* stop DMA */ + isa_dmadone( + ppc->ppc_dmaflags, ppc->ppc_dmaddr, + ppc->ppc_dmacnt, ppc->ppc_dmachan); + + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + goto error; + } + + /* wait for an empty fifo */ + while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + + for (spin=100; spin; spin--) + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + goto fifo_empty; +#ifdef PPC_DEBUG + printf("Z"); +#endif + error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100); + if (error != EWOULDBLOCK) { +#ifdef PPC_DEBUG + printf("I"); +#endif + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + error = EINTR; + goto error; + } + } + +fifo_empty: + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + } else + error = EINVAL; /* XXX we should FIFO and + * interrupts */ + } else + error = EINVAL; + +error: + + /* PDRQ must be kept unasserted until nPDACK is + * deasserted for a minimum of 350ns (SMC datasheet) + * + * Consequence may be a FIFO that never empty + */ + DELAY(1); + + w_ecr(ppc, ecr_sav); + w_ctr(ppc, ctr_sav); + + return (error); +} + /* * Configure current operating mode */ @@ -1209,32 +1571,34 @@ static int ppc_generic_setmode(int unit, int mode) { struct ppc_data *ppc = ppcdata[unit]; - - /* back to compatible mode, XXX don't know yet what to do here */ - if (mode == 0) { - ppc->ppc_mode = PPB_COMPATIBLE; - return (0); - } + u_char ecr = 0; /* check if mode is available */ - if (!(ppc->ppc_avm & mode)) - return (EOPNOTSUPP); + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); /* if ECP mode, configure ecr register */ if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; - /* XXX disable DMA, enable interrupts */ if (mode & PPB_EPP) - return (EOPNOTSUPP); - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - w_ecr(ppc, 0x20); + return (EINVAL); else if (mode & PPB_ECP) /* select ECP mode */ - w_ecr(ppc, 0x60); + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; else - /* select standard parallel port mode */ - w_ecr(ppc, 0x00); + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); } ppc->ppc_mode = mode; @@ -1242,42 +1606,50 @@ ppc_generic_setmode(int unit, int mode) return (0); } +/* + * The ppc driver is free to choose options like FIFO or DMA + * if ECP mode is available. + * + * The 'RAW' option allows the upper drivers to force the ppc mode + * even with FIFO, DMA available. + */ int ppc_smclike_setmode(int unit, int mode) { struct ppc_data *ppc = ppcdata[unit]; - - /* back to compatible mode, XXX don't know yet what to do here */ - if (mode == 0) { - ppc->ppc_mode = PPB_COMPATIBLE; - return (0); - } + u_char ecr = 0; /* check if mode is available */ - if (!(ppc->ppc_avm & mode)) - return (EOPNOTSUPP); + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); /* if ECP mode, configure ecr register */ if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP or EPP mode + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; - /* XXX disable DMA, enable interrupts */ if (mode & PPB_EPP) /* select EPP mode */ - w_ecr(ppc, 0x80); - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - w_ecr(ppc, 0x20); + ecr |= PPC_ECR_EPP; else if (mode & PPB_ECP) /* select ECP mode */ - w_ecr(ppc, 0x60); + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; else - /* select standard parallel port mode */ - w_ecr(ppc, 0x00); + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); } ppc->ppc_mode = mode; - return (0); } @@ -1314,8 +1686,9 @@ ppcprobe(struct isa_device *dvp) if((next_bios_ppc < BIOS_MAX_PPC) && (*(BIOS_PORTS+next_bios_ppc) != 0) ) { dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++); - printf("ppc: parallel port found at 0x%x\n", - dvp->id_iobase); + if (bootverbose) + printf("ppc: parallel port found at 0x%x\n", + dvp->id_iobase); } else return (0); } @@ -1351,6 +1724,8 @@ ppcprobe(struct isa_device *dvp) if (!(dvp->id_flags & 0x20)) ppc->ppc_irq = ffs(dvp->id_irq) - 1; + ppc->ppc_dmachan = dvp->id_drq; + ppcdata[ppc->ppc_unit] = ppc; nppc ++; @@ -1384,6 +1759,11 @@ ppcattach(struct isa_device *isdp) ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? ppc_epp_protocol[ppc->ppc_epp] : ""); + if (ppc->ppc_fifo) + printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n", + ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr, + ppc->ppc_rthr); + isdp->id_ointr = ppcintr; /* @@ -1397,6 +1777,13 @@ ppcattach(struct isa_device *isdp) ppc->ppc_link.ppbus = ppbus; ppbus->ppb_link = &ppc->ppc_link; + if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { + + /* acquire the DMA channel forever */ + isa_dma_acquire(ppc->ppc_dmachan); + isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ + } + /* * Probe the ppbus and attach devices found. */ diff --git a/sys/i386/isa/ppcreg.h b/sys/i386/isa/ppcreg.h index af8c9db..829bfd1 100644 --- a/sys/i386/isa/ppcreg.h +++ b/sys/i386/isa/ppcreg.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppcreg.h,v 1.4 1998/09/13 18:26:44 nsouch Exp $ + * $Id: ppcreg.h,v 1.5 1998/10/31 11:37:09 nsouch Exp $ * */ #ifndef __PPCREG_H @@ -55,6 +55,34 @@ struct ppc_data { int ppc_mode; /* chipset current mode */ int ppc_avm; /* chipset available modes */ +#define PPC_IRQ_NONE 0x0 +#define PPC_IRQ_nACK 0x1 +#define PPC_IRQ_DMA 0x2 +#define PPC_IRQ_FIFO 0x4 +#define PPC_IRQ_nFAULT 0x8 + int ppc_irqstat; /* remind irq settings */ + +#define PPC_DMA_INIT 0x01 +#define PPC_DMA_STARTED 0x02 +#define PPC_DMA_COMPLETE 0x03 +#define PPC_DMA_INTERRUPTED 0x04 +#define PPC_DMA_ERROR 0x05 + int ppc_dmastat; /* dma state */ + int ppc_dmachan; /* dma channel */ + int ppc_dmaflags; /* dma transfer flags */ + caddr_t ppc_dmaddr; /* buffer address */ + u_int ppc_dmacnt; /* count of bytes sent with dma */ + +#define PPC_PWORD_MASK 0x30 +#define PPC_PWORD_16 0x00 +#define PPC_PWORD_8 0x10 +#define PPC_PWORD_32 0x20 + char ppc_pword; /* PWord size */ + short ppc_fifo; /* FIFO threshold */ + + short ppc_wthr; /* writeIntrThresold */ + short ppc_rthr; /* readIntrThresold */ + #define ppc_base ppc_link.base #define ppc_epp ppc_link.epp_protocol #define ppc_irq ppc_link.id_irq @@ -71,25 +99,44 @@ struct ppc_data { * Parallel Port Chipset registers. */ #define PPC_SPP_DTR 0 /* SPP data register */ +#define PPC_ECP_A_FIFO 0 /* ECP Address fifo register */ #define PPC_SPP_STR 1 /* SPP status register */ #define PPC_SPP_CTR 2 /* SPP control register */ #define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */ -#define PPC_ECP_FIFO 0x400 /* ECP fifo register */ +#define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */ +#define PPC_ECP_CNFGA 0x400 /* Configuration register A */ +#define PPC_ECP_CNFGB 0x401 /* Configuration register B */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ +#define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */ +#define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */ +#define PPC_SERVICE_INTR 0x4 /* ecr register - bit 2 */ +#define PPC_ENABLE_DMA 0x8 /* ecr register - bit 3 */ +#define PPC_nFAULT_INTR 0x10 /* ecr register - bit 4 */ +#define PPC_ECR_STD 0x0 +#define PPC_ECR_PS2 0x20 +#define PPC_ECR_FIFO 0x40 +#define PPC_ECR_ECP 0x60 +#define PPC_ECR_EPP 0x80 + +#define PPC_DISABLE_INTR (PPC_SERVICE_INTR | PPC_nFAULT_INTR) +#define PPC_ECR_RESET (PPC_ECR_PS2 | PPC_DISABLE_INTR) + #define r_dtr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_DTR)) #define r_str(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_STR)) #define r_ctr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_CTR)) #define r_epp(ppc) ((char)inb((ppc)->ppc_base + PPC_EPP_DATA)) +#define r_cnfgA(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGA)) +#define r_cnfgB(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGB)) #define r_ecr(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_ECR)) -#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_FIFO)) +#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_D_FIFO)) #define w_dtr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_DTR, byte) #define w_str(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_STR, byte) #define w_ctr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_CTR, byte) #define w_epp(ppc,byte) outb((ppc)->ppc_base + PPC_EPP_DATA, byte) #define w_ecr(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_ECR, byte) -#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_FIFO, byte) +#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_D_FIFO, byte) /* * Register defines for the PC873xx parts diff --git a/sys/isa/ppc.c b/sys/isa/ppc.c index e351688..d2bc7a1 100644 --- a/sys/isa/ppc.c +++ b/sys/isa/ppc.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppc.c,v 1.12 1998/12/07 21:58:22 archie Exp $ + * $Id: ppc.c,v 1.13 1998/12/30 00:37:42 hoek Exp $ * */ #include "ppc.h" @@ -49,6 +49,11 @@ #include <i386/isa/ppcreg.h> +#include "opt_ppc.h" + +#define LOG_PPC(function, ppc, string) \ + if (bootverbose) printf("%s: %s\n", function, string) + static int ppcprobe(struct isa_device *); static int ppcattach(struct isa_device *); @@ -130,6 +135,9 @@ static int ppc_exec_microseq(int, struct ppb_microseq **); static int ppc_generic_setmode(int, int); static int ppc_smclike_setmode(int, int); +static int ppc_read(int, char *, int, int); +static int ppc_write(int, char *, int, int); + static struct ppb_adapter ppc_smclike_adapter = { 0, /* no intr handler, filled by chipset dependent code */ @@ -138,7 +146,7 @@ static struct ppb_adapter ppc_smclike_adapter = { ppc_exec_microseq, - ppc_smclike_setmode, + ppc_smclike_setmode, ppc_read, ppc_write, ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -155,7 +163,7 @@ static struct ppb_adapter ppc_generic_adapter = { ppc_exec_microseq, - ppc_generic_setmode, + ppc_generic_setmode, ppc_read, ppc_write, ppc_outsb_epp, ppc_outsw_epp, ppc_outsl_epp, ppc_insb_epp, ppc_insw_epp, ppc_insl_epp, @@ -173,8 +181,11 @@ ppc_ecp_sync(int unit) { struct ppc_data *ppc = ppcdata[unit]; int i, r; + if (!(ppc->ppc_avm & PPB_ECP)) + return; + r = r_ecr(ppc); - if ((r & 0xe0) != 0x80) + if ((r & 0xe0) != PPC_ECR_EPP) return; for (i = 0; i < 100; i++) { @@ -190,13 +201,115 @@ ppc_ecp_sync(int unit) { return; } -static void -ppcintr(int unit) +/* + * ppc_detect_fifo() + * + * Detect parallel port FIFO + */ +static int +ppc_detect_fifo(struct ppc_data *ppc) { - /* call directly upper code */ - ppb_intr(&ppcdata[unit]->ppc_link); + char ecr_sav; + char ctr_sav, ctr, cc; + short i; + + /* save registers */ + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); - return; + /* enter ECP configuration mode, no interrupt, no DMA */ + w_ecr(ppc, 0xf4); + + /* read PWord size - transfers in FIFO mode must be PWord aligned */ + ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK); + + /* XXX 16 and 32 bits implementations not supported */ + if (ppc->ppc_pword != PPC_PWORD_8) { + LOG_PPC(__FUNCTION__, ppc, "PWord not supported"); + goto error; + } + + w_ecr(ppc, 0x34); /* byte mode, no interrupt, no DMA */ + ctr = r_ctr(ppc); + w_ctr(ppc, ctr | PCD); /* set direction to 1 */ + + /* enter ECP test mode, no interrupt, no DMA */ + w_ecr(ppc, 0xd4); + + /* flush the FIFO */ + for (i=0; i<1024; i++) { + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + break; + cc = r_fifo(ppc); + } + + if (i >= 1024) { + LOG_PPC(__FUNCTION__, ppc, "can't flush FIFO"); + goto error; + } + + /* enable interrupts, no DMA */ + w_ecr(ppc, 0xd0); + + /* determine readIntrThreshold + * fill the FIFO until serviceIntr is set + */ + for (i=0; i<1024; i++) { + w_fifo(ppc, (char)i); + if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) { + /* readThreshold reached */ + ppc->ppc_rthr = i+1; + } + if (r_ecr(ppc) & PPC_FIFO_FULL) { + ppc->ppc_fifo = i+1; + break; + } + } + + if (i >= 1024) { + LOG_PPC(__FUNCTION__, ppc, "can't fill FIFO"); + goto error; + } + + w_ecr(ppc, 0xd4); /* test mode, no interrupt, no DMA */ + w_ctr(ppc, ctr & ~PCD); /* set direction to 0 */ + w_ecr(ppc, 0xd0); /* enable interrupts */ + + /* determine writeIntrThreshold + * empty the FIFO until serviceIntr is set + */ + for (i=ppc->ppc_fifo; i>0; i--) { + if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) { + LOG_PPC(__FUNCTION__, ppc, "invalid data in FIFO"); + goto error; + } + if (r_ecr(ppc) & PPC_SERVICE_INTR) { + /* writeIntrThreshold reached */ + ppc->ppc_wthr = ppc->ppc_fifo - i+1; + } + /* if FIFO empty before the last byte, error */ + if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) { + LOG_PPC(__FUNCTION__, ppc, "data lost in FIFO"); + goto error; + } + } + + /* FIFO must be empty after the last byte */ + if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + LOG_PPC(__FUNCTION__, ppc, "can't empty the FIFO"); + goto error; + } + + w_ctr(ppc, ctr_sav); + w_ecr(ppc, ecr_sav); + + return (0); + +error: + w_ctr(ppc, ctr_sav); + w_ecr(ppc, ecr_sav); + + return (EINVAL); } static int @@ -654,6 +767,13 @@ config: ppc->ppc_avm = chipset_mode; } + /* set FIFO threshold to 16 */ + if (ppc->ppc_avm & PPB_ECP) { + /* select CRA */ + outb(csr, 0xa); + outb(cio, 16); + } + end_detect: if (bootverbose) @@ -884,14 +1004,14 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) if (!chipset_mode) { /* first, check for ECP */ - w_ecr(ppc, 0x20); - if ((r_ecr(ppc) & 0xe0) == 0x20) { + w_ecr(ppc, PPC_ECR_PS2); + if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) { ppc->ppc_avm |= PPB_ECP | PPB_SPP; if (bootverbose) printf(" ECP SPP"); /* search for SMC style ECP+EPP mode */ - w_ecr(ppc, 0x80); + w_ecr(ppc, PPC_ECR_EPP); } /* try to reset EPP timeout bit */ @@ -911,7 +1031,7 @@ ppc_generic_detect(struct ppc_data *ppc, int chipset_mode) } } else { /* restore to standard mode */ - w_ecr(ppc, 0x0); + w_ecr(ppc, PPC_ECR_STD); } /* XXX try to detect NIBBLE and PS2 modes */ @@ -978,6 +1098,10 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) { } } + /* configure/detect ECP FIFO */ + if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80)) + ppc_detect_fifo(ppc); + return (0); } @@ -1202,6 +1326,244 @@ ppc_exec_microseq(int unit, struct ppb_microseq **p_msq) /* unreached */ } +static void +ppcintr(int unit) +{ + struct ppc_data *ppc = ppcdata[unit]; + char ctr, ecr; + + ctr = r_ctr(ppc); + ecr = r_ecr(ppc); + +#ifdef PPC_DEBUG + printf("!"); +#endif + + /* don't use ecp mode with IRQENABLE set */ + if (ctr & IRQENABLE) { + /* call upper code */ + ppb_intr(&ppc->ppc_link); + return; + } + + if (ctr & nFAULT) { + if (ppc->ppc_irqstat & PPC_IRQ_nFAULT) { + + w_ecr(ppc, ecr | PPC_nFAULT_INTR); + ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT; + } else { + /* call upper code */ + ppb_intr(&ppc->ppc_link); + return; + } + } + + if (ppc->ppc_irqstat & PPC_IRQ_DMA) { + /* disable interrupts (should be done by hardware though) */ + w_ecr(ppc, ecr | PPC_SERVICE_INTR); + ppc->ppc_irqstat &= ~PPC_IRQ_DMA; + ecr = r_ecr(ppc); + + /* check if DMA completed */ + if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) { +#ifdef PPC_DEBUG + printf("a"); +#endif + /* stop DMA */ + w_ecr(ppc, ecr & ~PPC_ENABLE_DMA); + ecr = r_ecr(ppc); + + if (ppc->ppc_dmastat == PPC_DMA_STARTED) { +#ifdef PPC_DEBUG + printf("d"); +#endif + isa_dmadone( + ppc->ppc_dmaflags, + ppc->ppc_dmaddr, + ppc->ppc_dmacnt, + ppc->ppc_dmachan); + + ppc->ppc_dmastat = PPC_DMA_COMPLETE; + + /* wakeup the waiting process */ + wakeup((caddr_t)ppc); + } + } + } else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) { + + /* classic interrupt I/O */ + ppc->ppc_irqstat &= ~PPC_IRQ_FIFO; + + } + + return; +} + +static int +ppc_read(int unit, char *buf, int len, int mode) +{ + return (EINVAL); +} + +/* + * Call this function if you want to send data in any advanced mode + * of your parallel port: FIFO, DMA + * + * If what you want is not possible (no ECP, no DMA...), + * EINVAL is returned + */ +static int +ppc_write(int unit, char *buf, int len, int how) +{ + struct ppc_data *ppc = ppcdata[unit]; + char ecr, ecr_sav, ctr, ctr_sav; + int s, error = 0; + int spin; + +#ifdef PPC_DEBUG + printf("w"); +#endif + + ecr_sav = r_ecr(ppc); + ctr_sav = r_ctr(ppc); + + /* + * Send buffer with DMA, FIFO and interrupts + */ + if (ppc->ppc_avm & PPB_ECP) { + + if (ppc->ppc_dmachan >= 0) { + + /* byte mode, no intr, no DMA, dir=0, flush fifo + */ + ecr = PPC_ECR_STD | PPC_DISABLE_INTR; + w_ecr(ppc, ecr); + + /* disable nAck interrupts */ + ctr = r_ctr(ppc); + ctr &= ~IRQENABLE; + w_ctr(ppc, ctr); + + ppc->ppc_dmaflags = 0; + ppc->ppc_dmaddr = (caddr_t)buf; + ppc->ppc_dmacnt = (u_int)len; + + switch (ppc->ppc_mode) { + case PPB_COMPATIBLE: + /* compatible mode with FIFO, no intr, DMA, dir=0 */ + ecr = PPC_ECR_FIFO | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + case PPB_ECP: + ecr = PPC_ECR_ECP | PPC_DISABLE_INTR | PPC_ENABLE_DMA; + break; + default: + error = EINVAL; + goto error; + } + + w_ecr(ppc, ecr); + ecr = r_ecr(ppc); + + /* enter splhigh() not to be preempted + * by the dma interrupt, we may miss + * the wakeup otherwise + */ + s = splhigh(); + + ppc->ppc_dmastat = PPC_DMA_INIT; + + /* enable interrupts */ + ecr &= ~PPC_SERVICE_INTR; + ppc->ppc_irqstat = PPC_IRQ_DMA; + w_ecr(ppc, ecr); + + isa_dmastart( + ppc->ppc_dmaflags, + ppc->ppc_dmaddr, + ppc->ppc_dmacnt, + ppc->ppc_dmachan); +#ifdef PPC_DEBUG + printf("s%d", ppc->ppc_dmacnt); +#endif + ppc->ppc_dmastat = PPC_DMA_STARTED; + + /* Wait for the DMA completed interrupt. We hope we won't + * miss it, otherwise a signal will be necessary to unlock the + * process. + */ + do { + /* release CPU */ + error = tsleep((caddr_t)ppc, + PPBPRI | PCATCH, "ppcdma", 0); + + } while (error == EWOULDBLOCK); + + splx(s); + + if (error) { +#ifdef PPC_DEBUG + printf("i"); +#endif + /* stop DMA */ + isa_dmadone( + ppc->ppc_dmaflags, ppc->ppc_dmaddr, + ppc->ppc_dmacnt, ppc->ppc_dmachan); + + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + goto error; + } + + /* wait for an empty fifo */ + while (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) { + + for (spin=100; spin; spin--) + if (r_ecr(ppc) & PPC_FIFO_EMPTY) + goto fifo_empty; +#ifdef PPC_DEBUG + printf("Z"); +#endif + error = tsleep((caddr_t)ppc, PPBPRI | PCATCH, "ppcfifo", hz/100); + if (error != EWOULDBLOCK) { +#ifdef PPC_DEBUG + printf("I"); +#endif + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + ppc->ppc_dmastat = PPC_DMA_INTERRUPTED; + error = EINTR; + goto error; + } + } + +fifo_empty: + /* no dma, no interrupt, flush the fifo */ + w_ecr(ppc, PPC_ECR_RESET); + + } else + error = EINVAL; /* XXX we should FIFO and + * interrupts */ + } else + error = EINVAL; + +error: + + /* PDRQ must be kept unasserted until nPDACK is + * deasserted for a minimum of 350ns (SMC datasheet) + * + * Consequence may be a FIFO that never empty + */ + DELAY(1); + + w_ecr(ppc, ecr_sav); + w_ctr(ppc, ctr_sav); + + return (error); +} + /* * Configure current operating mode */ @@ -1209,32 +1571,34 @@ static int ppc_generic_setmode(int unit, int mode) { struct ppc_data *ppc = ppcdata[unit]; - - /* back to compatible mode, XXX don't know yet what to do here */ - if (mode == 0) { - ppc->ppc_mode = PPB_COMPATIBLE; - return (0); - } + u_char ecr = 0; /* check if mode is available */ - if (!(ppc->ppc_avm & mode)) - return (EOPNOTSUPP); + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); /* if ECP mode, configure ecr register */ if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; - /* XXX disable DMA, enable interrupts */ if (mode & PPB_EPP) - return (EOPNOTSUPP); - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - w_ecr(ppc, 0x20); + return (EINVAL); else if (mode & PPB_ECP) /* select ECP mode */ - w_ecr(ppc, 0x60); + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; else - /* select standard parallel port mode */ - w_ecr(ppc, 0x00); + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); } ppc->ppc_mode = mode; @@ -1242,42 +1606,50 @@ ppc_generic_setmode(int unit, int mode) return (0); } +/* + * The ppc driver is free to choose options like FIFO or DMA + * if ECP mode is available. + * + * The 'RAW' option allows the upper drivers to force the ppc mode + * even with FIFO, DMA available. + */ int ppc_smclike_setmode(int unit, int mode) { struct ppc_data *ppc = ppcdata[unit]; - - /* back to compatible mode, XXX don't know yet what to do here */ - if (mode == 0) { - ppc->ppc_mode = PPB_COMPATIBLE; - return (0); - } + u_char ecr = 0; /* check if mode is available */ - if (!(ppc->ppc_avm & mode)) - return (EOPNOTSUPP); + if (mode && !(ppc->ppc_avm & mode)) + return (EINVAL); /* if ECP mode, configure ecr register */ if (ppc->ppc_avm & PPB_ECP) { + /* return to byte mode (keeping direction bit), + * no interrupt, no DMA to be able to change to + * ECP or EPP mode + */ + w_ecr(ppc, PPC_ECR_RESET); + ecr = PPC_DISABLE_INTR; - /* XXX disable DMA, enable interrupts */ if (mode & PPB_EPP) /* select EPP mode */ - w_ecr(ppc, 0x80); - else if (mode & PPB_PS2) - /* select PS2 mode with ECP */ - w_ecr(ppc, 0x20); + ecr |= PPC_ECR_EPP; else if (mode & PPB_ECP) /* select ECP mode */ - w_ecr(ppc, 0x60); + ecr |= PPC_ECR_ECP; + else if (mode & PPB_PS2) + /* select PS2 mode with ECP */ + ecr |= PPC_ECR_PS2; else - /* select standard parallel port mode */ - w_ecr(ppc, 0x00); + /* select COMPATIBLE/NIBBLE mode */ + ecr |= PPC_ECR_STD; + + w_ecr(ppc, ecr); } ppc->ppc_mode = mode; - return (0); } @@ -1314,8 +1686,9 @@ ppcprobe(struct isa_device *dvp) if((next_bios_ppc < BIOS_MAX_PPC) && (*(BIOS_PORTS+next_bios_ppc) != 0) ) { dvp->id_iobase = *(BIOS_PORTS+next_bios_ppc++); - printf("ppc: parallel port found at 0x%x\n", - dvp->id_iobase); + if (bootverbose) + printf("ppc: parallel port found at 0x%x\n", + dvp->id_iobase); } else return (0); } @@ -1351,6 +1724,8 @@ ppcprobe(struct isa_device *dvp) if (!(dvp->id_flags & 0x20)) ppc->ppc_irq = ffs(dvp->id_irq) - 1; + ppc->ppc_dmachan = dvp->id_drq; + ppcdata[ppc->ppc_unit] = ppc; nppc ++; @@ -1384,6 +1759,11 @@ ppcattach(struct isa_device *isdp) ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ? ppc_epp_protocol[ppc->ppc_epp] : ""); + if (ppc->ppc_fifo) + printf("ppc%d: FIFO with %d/%d/%d bytes threshold\n", + ppc->ppc_unit, ppc->ppc_fifo, ppc->ppc_wthr, + ppc->ppc_rthr); + isdp->id_ointr = ppcintr; /* @@ -1397,6 +1777,13 @@ ppcattach(struct isa_device *isdp) ppc->ppc_link.ppbus = ppbus; ppbus->ppb_link = &ppc->ppc_link; + if ((ppc->ppc_avm & PPB_ECP) && (ppc->ppc_dmachan > 0)) { + + /* acquire the DMA channel forever */ + isa_dma_acquire(ppc->ppc_dmachan); + isa_dmainit(ppc->ppc_dmachan, 1024); /* nlpt.BUFSIZE */ + } + /* * Probe the ppbus and attach devices found. */ diff --git a/sys/isa/ppcreg.h b/sys/isa/ppcreg.h index af8c9db..829bfd1 100644 --- a/sys/isa/ppcreg.h +++ b/sys/isa/ppcreg.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ppcreg.h,v 1.4 1998/09/13 18:26:44 nsouch Exp $ + * $Id: ppcreg.h,v 1.5 1998/10/31 11:37:09 nsouch Exp $ * */ #ifndef __PPCREG_H @@ -55,6 +55,34 @@ struct ppc_data { int ppc_mode; /* chipset current mode */ int ppc_avm; /* chipset available modes */ +#define PPC_IRQ_NONE 0x0 +#define PPC_IRQ_nACK 0x1 +#define PPC_IRQ_DMA 0x2 +#define PPC_IRQ_FIFO 0x4 +#define PPC_IRQ_nFAULT 0x8 + int ppc_irqstat; /* remind irq settings */ + +#define PPC_DMA_INIT 0x01 +#define PPC_DMA_STARTED 0x02 +#define PPC_DMA_COMPLETE 0x03 +#define PPC_DMA_INTERRUPTED 0x04 +#define PPC_DMA_ERROR 0x05 + int ppc_dmastat; /* dma state */ + int ppc_dmachan; /* dma channel */ + int ppc_dmaflags; /* dma transfer flags */ + caddr_t ppc_dmaddr; /* buffer address */ + u_int ppc_dmacnt; /* count of bytes sent with dma */ + +#define PPC_PWORD_MASK 0x30 +#define PPC_PWORD_16 0x00 +#define PPC_PWORD_8 0x10 +#define PPC_PWORD_32 0x20 + char ppc_pword; /* PWord size */ + short ppc_fifo; /* FIFO threshold */ + + short ppc_wthr; /* writeIntrThresold */ + short ppc_rthr; /* readIntrThresold */ + #define ppc_base ppc_link.base #define ppc_epp ppc_link.epp_protocol #define ppc_irq ppc_link.id_irq @@ -71,25 +99,44 @@ struct ppc_data { * Parallel Port Chipset registers. */ #define PPC_SPP_DTR 0 /* SPP data register */ +#define PPC_ECP_A_FIFO 0 /* ECP Address fifo register */ #define PPC_SPP_STR 1 /* SPP status register */ #define PPC_SPP_CTR 2 /* SPP control register */ #define PPC_EPP_DATA 4 /* EPP data register (8, 16 or 32 bit) */ -#define PPC_ECP_FIFO 0x400 /* ECP fifo register */ +#define PPC_ECP_D_FIFO 0x400 /* ECP Data fifo register */ +#define PPC_ECP_CNFGA 0x400 /* Configuration register A */ +#define PPC_ECP_CNFGB 0x401 /* Configuration register B */ #define PPC_ECP_ECR 0x402 /* ECP extended control register */ +#define PPC_FIFO_EMPTY 0x1 /* ecr register - bit 0 */ +#define PPC_FIFO_FULL 0x2 /* ecr register - bit 1 */ +#define PPC_SERVICE_INTR 0x4 /* ecr register - bit 2 */ +#define PPC_ENABLE_DMA 0x8 /* ecr register - bit 3 */ +#define PPC_nFAULT_INTR 0x10 /* ecr register - bit 4 */ +#define PPC_ECR_STD 0x0 +#define PPC_ECR_PS2 0x20 +#define PPC_ECR_FIFO 0x40 +#define PPC_ECR_ECP 0x60 +#define PPC_ECR_EPP 0x80 + +#define PPC_DISABLE_INTR (PPC_SERVICE_INTR | PPC_nFAULT_INTR) +#define PPC_ECR_RESET (PPC_ECR_PS2 | PPC_DISABLE_INTR) + #define r_dtr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_DTR)) #define r_str(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_STR)) #define r_ctr(ppc) ((char)inb((ppc)->ppc_base + PPC_SPP_CTR)) #define r_epp(ppc) ((char)inb((ppc)->ppc_base + PPC_EPP_DATA)) +#define r_cnfgA(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGA)) +#define r_cnfgB(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_CNFGB)) #define r_ecr(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_ECR)) -#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_FIFO)) +#define r_fifo(ppc) ((char)inb((ppc)->ppc_base + PPC_ECP_D_FIFO)) #define w_dtr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_DTR, byte) #define w_str(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_STR, byte) #define w_ctr(ppc,byte) outb((ppc)->ppc_base + PPC_SPP_CTR, byte) #define w_epp(ppc,byte) outb((ppc)->ppc_base + PPC_EPP_DATA, byte) #define w_ecr(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_ECR, byte) -#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_FIFO, byte) +#define w_fifo(ppc,byte) outb((ppc)->ppc_base + PPC_ECP_D_FIFO, byte) /* * Register defines for the PC873xx parts diff --git a/usr.sbin/lptcontrol/lptcontrol.c b/usr.sbin/lptcontrol/lptcontrol.c index 73ea05c..afd234e 100644 --- a/usr.sbin/lptcontrol/lptcontrol.c +++ b/usr.sbin/lptcontrol/lptcontrol.c @@ -30,7 +30,7 @@ #ifndef lint static const char rcsid[] = - "$Id$"; + "$Id: lptcontrol.c,v 1.6 1997/09/25 06:36:29 charnier Exp $"; #endif /* not lint */ #include <ctype.h> @@ -53,10 +53,12 @@ static const char rcsid[] = #define IRQ_INVALID -1 #define DO_POLL 0 #define USE_IRQ 1 +#define USE_EXT_MODE 2 +#define USE_STD_MODE 3 static void usage() { - fprintf(stderr, "usage: lptcontrol -i | -p [-u <unit no.>]\n"); + fprintf(stderr, "usage: lptcontrol -i | -p | -s | -e [-u <unit no.>]\n"); exit(1); } @@ -89,10 +91,12 @@ int main (int argc, char * argv[]) int irq_status = IRQ_INVALID; char * unit = DEFAULT_UNIT; - while((opt = getopt(argc, argv, "ipu:")) != -1) + while((opt = getopt(argc, argv, "ipesu:")) != -1) switch(opt) { case 'i': irq_status = USE_IRQ; break; case 'p': irq_status = DO_POLL; break; + case 'e': irq_status = USE_EXT_MODE; break; + case 's': irq_status = USE_STD_MODE; break; case 'u': unit = optarg; if(!isdigit(*unit)) usage(); |