summaryrefslogtreecommitdiffstats
path: root/sys/dev/ppbus
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>1998-08-03 19:14:33 +0000
committermsmith <msmith@FreeBSD.org>1998-08-03 19:14:33 +0000
commit2fdb23234a18b64b994328bb4be606a420e3b56d (patch)
treef8a4d5d9ba99cb367e9263ec96d08ecb8919f529 /sys/dev/ppbus
parentcfef94c8ca2347c78a745676fe6061eb1f172be7 (diff)
downloadFreeBSD-src-2fdb23234a18b64b994328bb4be606a420e3b56d.zip
FreeBSD-src-2fdb23234a18b64b994328bb4be606a420e3b56d.tar.gz
Major ppbus updates from the author.
- ppbus now supports PLIP via the if_plip driver - ieee1284 infrastructure added, including parallel-port PnP - port microsequencer added, for scripting the sort of port I/O that is common with parallel devices without endless calls up and down through the driver structure. - improved bus ownership behaviour among the ppbus-using drivers. - improved I/O chipset feature detection The vpo driver is now implemented using the microsequencer, leading to some performance improvements as well as providing an extensive example of its use. Reviewed by: msmith Submitted by: Nicolas Souchu <Nicolas.Souchu@prism.uvsq.fr>
Diffstat (limited to 'sys/dev/ppbus')
-rw-r--r--sys/dev/ppbus/if_plip.c772
-rw-r--r--sys/dev/ppbus/nlpt.c61
-rw-r--r--sys/dev/ppbus/ppb_1284.c72
-rw-r--r--sys/dev/ppbus/ppb_1284.h9
-rw-r--r--sys/dev/ppbus/ppb_base.c63
-rw-r--r--sys/dev/ppbus/ppb_msq.c326
-rw-r--r--sys/dev/ppbus/ppb_msq.h190
-rw-r--r--sys/dev/ppbus/ppbconf.c92
-rw-r--r--sys/dev/ppbus/ppbconf.h168
-rw-r--r--sys/dev/ppbus/pps.c3
-rw-r--r--sys/dev/ppbus/vpo.c626
-rw-r--r--sys/dev/ppbus/vpoio.c771
-rw-r--r--sys/dev/ppbus/vpoio.h (renamed from sys/dev/ppbus/vpo.h)82
13 files changed, 2447 insertions, 788 deletions
diff --git a/sys/dev/ppbus/if_plip.c b/sys/dev/ppbus/if_plip.c
new file mode 100644
index 0000000..b25abfc
--- /dev/null
+++ b/sys/dev/ppbus/if_plip.c
@@ -0,0 +1,772 @@
+/*-
+ * Copyright (c) 1997 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
+ * $Id$
+ */
+
+/*
+ * Parallel port TCP/IP interfaces added. I looked at the driver from
+ * MACH but this is a complete rewrite, and btw. incompatible, and it
+ * should perform better too. I have never run the MACH driver though.
+ *
+ * This driver sends two bytes (0x08, 0x00) in front of each packet,
+ * to allow us to distinguish another format later.
+ *
+ * Now added an Linux/Crynwr compatibility mode which is enabled using
+ * IF_LINK0 - Tim Wilkinson.
+ *
+ * TODO:
+ * Make HDLC/PPP mode, use IF_LLC1 to enable.
+ *
+ * Connect the two computers using a Laplink parallel cable to use this
+ * feature:
+ *
+ * +----------------------------------------+
+ * |A-name A-End B-End Descr. Port/Bit |
+ * +----------------------------------------+
+ * |DATA0 2 15 Data 0/0x01 |
+ * |-ERROR 15 2 1/0x08 |
+ * +----------------------------------------+
+ * |DATA1 3 13 Data 0/0x02 |
+ * |+SLCT 13 3 1/0x10 |
+ * +----------------------------------------+
+ * |DATA2 4 12 Data 0/0x04 |
+ * |+PE 12 4 1/0x20 |
+ * +----------------------------------------+
+ * |DATA3 5 10 Strobe 0/0x08 |
+ * |-ACK 10 5 1/0x40 |
+ * +----------------------------------------+
+ * |DATA4 6 11 Data 0/0x10 |
+ * |BUSY 11 6 1/~0x80 |
+ * +----------------------------------------+
+ * |GND 18-25 18-25 GND - |
+ * +----------------------------------------+
+ *
+ * Expect transfer-rates up to 75 kbyte/sec.
+ *
+ * If GCC could correctly grok
+ * register int port asm("edx")
+ * the code would be cleaner
+ *
+ * Poul-Henning Kamp <phk@freebsd.org>
+ */
+
+/*
+ * Update for ppbus, PLIP support only - Nicolas Souchu
+ */
+
+#ifdef KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+
+#endif /* KERNEL */
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/nlpt.h>
+
+#ifndef LPMTU /* MTU for the lp# interfaces */
+#define LPMTU 1500
+#endif
+
+#ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */
+#define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */
+#endif
+
+#ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */
+#define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */
+#endif
+
+#ifndef LPMAXERRS /* Max errors before !RUNNING */
+#define LPMAXERRS 100
+#endif
+
+#define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */
+#define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */
+#define MLPIPHDRLEN CLPIPHDRLEN
+
+#define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */
+#define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */
+#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
+#define MLPIPHDRLEN LPIPHDRLEN
+#endif
+
+#define LPIPTBLSIZE 256 /* Size of octet translation table */
+
+#define DEBUG
+
+#ifndef DEBUG
+#define lprintf (void)
+#else
+#define lprintf if (lptflag) printf
+static int volatile lptflag = 1;
+#endif
+
+struct lpt_softc {
+ unsigned short lp_unit;
+
+ struct ppb_device lp_dev;
+
+ struct ifnet sc_if;
+ u_char *sc_ifbuf;
+ int sc_iferrs;
+};
+
+static int nlp = 0;
+#define MAXPLIP 8 /* XXX not much better! */
+static struct lpt_softc *lpdata[MAXPLIP];
+
+
+/* Tables for the lp# interface */
+static u_char *txmith;
+#define txmitl (txmith+(1*LPIPTBLSIZE))
+#define trecvh (txmith+(2*LPIPTBLSIZE))
+#define trecvl (txmith+(3*LPIPTBLSIZE))
+
+static u_char *ctxmith;
+#define ctxmitl (ctxmith+(1*LPIPTBLSIZE))
+#define ctrecvh (ctxmith+(2*LPIPTBLSIZE))
+#define ctrecvl (ctxmith+(3*LPIPTBLSIZE))
+
+/* Functions for the lp# interface */
+static struct ppb_device *lpprobe(struct ppb_data *);
+static int lpattach(struct ppb_device *);
+
+static int lpinittables(void);
+static int lpioctl(struct ifnet *, u_long, caddr_t);
+static int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+static void lpintr(int);
+
+/*
+ * Make ourselves visible as a ppbus driver
+ */
+
+static struct ppb_driver lpdriver = {
+ lpprobe, lpattach, "lp"
+};
+DATA_SET(ppbdriver_set, lpdriver);
+
+
+/*
+ * lpprobe()
+ */
+static struct ppb_device *
+lpprobe(struct ppb_data *ppb)
+{
+ struct lpt_softc *lp;
+
+ /* if we haven't interrupts, the probe fails */
+ if (!ppb->ppb_link->id_irq)
+ return (0);
+
+ lp = (struct lpt_softc *) malloc(sizeof(struct lpt_softc),
+ M_TEMP, M_NOWAIT);
+ if (!lp) {
+ printf("lp: cannot malloc!\n");
+ return (0);
+ }
+ bzero(lp, sizeof(struct lpt_softc));
+
+ lpdata[nlp] = lp;
+
+ /*
+ * lp dependent initialisation.
+ */
+ lp->lp_unit = nlp;
+
+ if (bootverbose)
+ printf("plip: irq %d", ppb->ppb_link->id_irq);
+
+ /*
+ * ppbus dependent initialisation.
+ */
+ lp->lp_dev.id_unit = lp->lp_unit;
+ lp->lp_dev.name = lpdriver.name;
+ lp->lp_dev.ppb = ppb;
+ lp->lp_dev.intr = lpintr;
+
+ /* Ok, go to next device on next probe */
+ nlp ++;
+
+ return (&lp->lp_dev);
+}
+
+static int
+lpattach (struct ppb_device *dev)
+{
+ int unit = dev->id_unit;
+ struct lpt_softc *sc = lpdata[unit];
+ struct ifnet *ifp = &sc->sc_if;
+
+ /*
+ * Report ourselves
+ */
+ printf("plip%d: <PLIP network interface> on ppbus %d\n",
+ dev->id_unit, dev->ppb->ppb_link->adapter_unit);
+
+ ifp->if_softc = sc;
+ ifp->if_name = "lp";
+ ifp->if_unit = unit;
+ ifp->if_mtu = LPMTU;
+ ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
+ ifp->if_ioctl = lpioctl;
+ ifp->if_output = lpoutput;
+ ifp->if_type = IFT_PARA;
+ ifp->if_hdrlen = 0;
+ ifp->if_addrlen = 0;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+ if_attach(ifp);
+
+#if NBPFILTER > 0
+ bpfattach(ifp, DLT_NULL, LPIPHDRLEN);
+#endif
+
+ return (1);
+}
+/*
+ * Build the translation tables for the LPIP (BSD unix) protocol.
+ * We don't want to calculate these nasties in our tight loop, so we
+ * precalculate them when we initialize.
+ */
+static int
+lpinittables (void)
+{
+ int i;
+
+ if (!txmith)
+ txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
+
+ if (!txmith)
+ return 1;
+
+ if (!ctxmith)
+ ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
+
+ if (!ctxmith)
+ return 1;
+
+ for (i=0; i < LPIPTBLSIZE; i++) {
+ ctxmith[i] = (i & 0xF0) >> 4;
+ ctxmitl[i] = 0x10 | (i & 0x0F);
+ ctrecvh[i] = (i & 0x78) << 1;
+ ctrecvl[i] = (i & 0x78) >> 3;
+ }
+
+ for (i=0; i < LPIPTBLSIZE; i++) {
+ txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
+ txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
+ trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
+ trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Process an ioctl request.
+ */
+
+static int
+lpioctl (struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct lpt_softc *sc = lpdata[ifp->if_unit];
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ u_char *ptr;
+ int error;
+
+ switch (cmd) {
+
+ case SIOCSIFDSTADDR:
+ case SIOCAIFADDR:
+ case SIOCSIFADDR:
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ return EAFNOSUPPORT;
+
+ ifp->if_flags |= IFF_UP;
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) {
+
+ ppb_wctr(&sc->lp_dev, 0x00);
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* IFF_UP is not set, try to release the bus anyway */
+ ppb_release_bus(&sc->lp_dev);
+ break;
+ }
+ if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
+
+ /*
+ * Try to allocate the ppbus as soon as possible
+ * With ppbus allocation, interrupts are enabled
+ * Now IFF_UP means that we own the bus
+ *
+ * XXX
+ * Should the request be interruptible?
+ */
+ if ((error = ppb_request_bus(&sc->lp_dev, PPB_WAIT|PPB_INTR)))
+ return (error);
+
+ if (lpinittables()) {
+ ppb_release_bus(&sc->lp_dev);
+ return ENOBUFS;
+ }
+
+ sc->sc_ifbuf = malloc(sc->sc_if.if_mtu + MLPIPHDRLEN,
+ M_DEVBUF, M_WAITOK);
+ if (!sc->sc_ifbuf) {
+ ppb_release_bus(&sc->lp_dev);
+ return ENOBUFS;
+ }
+
+ ppb_wctr(&sc->lp_dev, LPC_ENA);
+ ifp->if_flags |= IFF_RUNNING;
+ }
+ break;
+
+ case SIOCSIFMTU:
+ ptr = sc->sc_ifbuf;
+ sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT);
+ if (!sc->sc_ifbuf) {
+ sc->sc_ifbuf = ptr;
+ return ENOBUFS;
+ }
+ if (ptr)
+ free(ptr,M_DEVBUF);
+ sc->sc_if.if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = sc->sc_if.if_mtu;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifr == 0) {
+ return EAFNOSUPPORT; /* XXX */
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+ case AF_INET:
+ break;
+
+ default:
+ return EAFNOSUPPORT;
+ }
+ break;
+
+ default:
+ lprintf("LP:ioctl(0x%x)\n",cmd);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static __inline int
+clpoutbyte (u_char byte, int spin, struct ppb_device *dev)
+{
+ ppb_wdtr(dev, ctxmitl[byte]);
+ while (ppb_rstr(dev) & CLPIP_SHAKE)
+ if (--spin == 0) {
+ return 1;
+ }
+ ppb_wdtr(dev, ctxmith[byte]);
+ while (!(ppb_rstr(dev) & CLPIP_SHAKE))
+ if (--spin == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static __inline int
+clpinbyte (int spin, struct ppb_device *dev)
+{
+ int c, cl;
+
+ while((ppb_rstr(dev) & CLPIP_SHAKE))
+ if(!--spin) {
+ return -1;
+ }
+ cl = ppb_rstr(dev);
+ ppb_wdtr(dev, 0x10);
+
+ while(!(ppb_rstr(dev) & CLPIP_SHAKE))
+ if(!--spin) {
+ return -1;
+ }
+ c = ppb_rstr(dev);
+ ppb_wdtr(dev, 0x00);
+
+ return (ctrecvl[cl] | ctrecvh[c]);
+}
+
+static void
+lpintr (int unit)
+{
+ struct lpt_softc *sc = lpdata[unit];
+ int len, s, j;
+ u_char *bp;
+ u_char c, cl;
+ struct mbuf *top;
+
+ s = splhigh();
+
+ if (sc->sc_if.if_flags & IFF_LINK0) {
+
+ /* Ack. the request */
+ ppb_wdtr(&sc->lp_dev, 0x01);
+
+ /* Get the packet length */
+ j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
+ if (j == -1)
+ goto err;
+ len = j;
+ j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
+ if (j == -1)
+ goto err;
+ len = len + (j << 8);
+ if (len > sc->sc_if.if_mtu + MLPIPHDRLEN)
+ goto err;
+
+ bp = sc->sc_ifbuf;
+
+ while (len--) {
+ j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
+ if (j == -1) {
+ goto err;
+ }
+ *bp++ = j;
+ }
+ /* Get and ignore checksum */
+ j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
+ if (j == -1) {
+ goto err;
+ }
+
+ len = bp - sc->sc_ifbuf;
+ if (len <= CLPIPHDRLEN)
+ goto err;
+
+ sc->sc_iferrs = 0;
+
+ if (IF_QFULL(&ipintrq)) {
+ lprintf("DROP");
+ IF_DROP(&ipintrq);
+ goto done;
+ }
+ len -= CLPIPHDRLEN;
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_ibytes += len;
+ top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, &sc->sc_if, 0);
+ if (top) {
+ IF_ENQUEUE(&ipintrq, top);
+ schednetisr(NETISR_IP);
+ }
+ goto done;
+ }
+ while ((ppb_rstr(&sc->lp_dev) & LPIP_SHAKE)) {
+ len = sc->sc_if.if_mtu + LPIPHDRLEN;
+ bp = sc->sc_ifbuf;
+ while (len--) {
+
+ cl = ppb_rstr(&sc->lp_dev);
+ ppb_wdtr(&sc->lp_dev, 8);
+
+ j = LPMAXSPIN2;
+ while((ppb_rstr(&sc->lp_dev) & LPIP_SHAKE))
+ if(!--j) goto err;
+
+ c = ppb_rstr(&sc->lp_dev);
+ ppb_wdtr(&sc->lp_dev, 0);
+
+ *bp++= trecvh[cl] | trecvl[c];
+
+ j = LPMAXSPIN2;
+ while (!((cl=ppb_rstr(&sc->lp_dev)) & LPIP_SHAKE)) {
+ if (cl != c &&
+ (((cl = ppb_rstr(&sc->lp_dev)) ^ 0xb8) & 0xf8) ==
+ (c & 0xf8))
+ goto end;
+ if (!--j) goto err;
+ }
+ }
+
+ end:
+ len = bp - sc->sc_ifbuf;
+ if (len <= LPIPHDRLEN)
+ goto err;
+
+ sc->sc_iferrs = 0;
+
+ if (IF_QFULL(&ipintrq)) {
+ lprintf("DROP");
+ IF_DROP(&ipintrq);
+ goto done;
+ }
+#if NBPFILTER > 0
+ if (sc->sc_if.if_bpf) {
+ bpf_tap(&sc->sc_if, sc->sc_ifbuf, len);
+ }
+#endif
+ len -= LPIPHDRLEN;
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_ibytes += len;
+ top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, &sc->sc_if, 0);
+ if (top) {
+ IF_ENQUEUE(&ipintrq, top);
+ schednetisr(NETISR_IP);
+ }
+ }
+ goto done;
+
+ err:
+ ppb_wdtr(&sc->lp_dev, 0);
+ lprintf("R");
+ sc->sc_if.if_ierrors++;
+ sc->sc_iferrs++;
+
+ /*
+ * We are not able to send receive anything for now,
+ * so stop wasting our time
+ */
+ if (sc->sc_iferrs > LPMAXERRS) {
+ printf("lp%d: Too many errors, Going off-line.\n", unit);
+ ppb_wctr(&sc->lp_dev, 0x00);
+ sc->sc_if.if_flags &= ~IFF_RUNNING;
+ sc->sc_iferrs=0;
+ }
+
+ done:
+ splx(s);
+ return;
+}
+
+static __inline int
+lpoutbyte (u_char byte, int spin, struct ppb_device *dev)
+{
+ ppb_wdtr(dev, txmith[byte]);
+ while (!(ppb_rstr(dev) & LPIP_SHAKE))
+ if (--spin == 0)
+ return 1;
+ ppb_wdtr(dev, txmitl[byte]);
+ while (ppb_rstr(dev) & LPIP_SHAKE)
+ if (--spin == 0)
+ return 1;
+ return 0;
+}
+
+static int
+lpoutput (struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt)
+{
+ struct lpt_softc *sc = lpdata[ifp->if_unit];
+ int s, err;
+ struct mbuf *mm;
+ u_char *cp = "\0\0";
+ u_char chksum = 0;
+ int count = 0;
+ int i;
+ int spin;
+
+ /* We need a sensible value if we abort */
+ cp++;
+ ifp->if_flags |= IFF_RUNNING;
+
+ err = 1; /* assume we're aborting because of an error */
+
+ s = splhigh();
+
+ /* Suspend (on laptops) or receive-errors might have taken us offline */
+ ppb_wctr(&sc->lp_dev, LPC_ENA);
+
+ if (ifp->if_flags & IFF_LINK0) {
+
+ if (!(ppb_rstr(&sc->lp_dev) & CLPIP_SHAKE)) {
+ lprintf("&");
+ lpintr(ifp->if_unit);
+ }
+
+ /* Alert other end to pending packet */
+ spin = LPMAXSPIN1;
+ ppb_wdtr(&sc->lp_dev, 0x08);
+ while ((ppb_rstr(&sc->lp_dev) & 0x08) == 0)
+ if (--spin == 0) {
+ goto nend;
+ }
+
+ /* Calculate length of packet, then send that */
+
+ count += 14; /* Ethernet header len */
+
+ mm = m;
+ for (mm = m; mm; mm = mm->m_next) {
+ count += mm->m_len;
+ }
+ if (clpoutbyte(count & 0xFF, LPMAXSPIN1, &sc->lp_dev))
+ goto nend;
+ if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, &sc->lp_dev))
+ goto nend;
+
+ /* Send dummy ethernet header */
+ for (i = 0; i < 12; i++) {
+ if (clpoutbyte(i, LPMAXSPIN1, &sc->lp_dev))
+ goto nend;
+ chksum += i;
+ }
+
+ if (clpoutbyte(0x08, LPMAXSPIN1, &sc->lp_dev))
+ goto nend;
+ if (clpoutbyte(0x00, LPMAXSPIN1, &sc->lp_dev))
+ goto nend;
+ chksum += 0x08 + 0x00; /* Add into checksum */
+
+ mm = m;
+ do {
+ cp = mtod(mm, u_char *);
+ while (mm->m_len--) {
+ chksum += *cp;
+ if (clpoutbyte(*cp++, LPMAXSPIN2, &sc->lp_dev))
+ goto nend;
+ }
+ } while ((mm = mm->m_next));
+
+ /* Send checksum */
+ if (clpoutbyte(chksum, LPMAXSPIN2, &sc->lp_dev))
+ goto nend;
+
+ /* Go quiescent */
+ ppb_wdtr(&sc->lp_dev, 0);
+
+ err = 0; /* No errors */
+
+ nend:
+ if (err) { /* if we didn't timeout... */
+ ifp->if_oerrors++;
+ lprintf("X");
+ } else {
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+ }
+
+ m_freem(m);
+
+ if (!(ppb_rstr(&sc->lp_dev) & CLPIP_SHAKE)) {
+ lprintf("^");
+ lpintr(ifp->if_unit);
+ }
+ (void) splx(s);
+ return 0;
+ }
+
+ if (ppb_rstr(&sc->lp_dev) & LPIP_SHAKE) {
+ lprintf("&");
+ lpintr(ifp->if_unit);
+ }
+
+ if (lpoutbyte(0x08, LPMAXSPIN1, &sc->lp_dev))
+ goto end;
+ if (lpoutbyte(0x00, LPMAXSPIN2, &sc->lp_dev))
+ goto end;
+
+ mm = m;
+ do {
+ cp = mtod(mm,u_char *);
+ while (mm->m_len--)
+ if (lpoutbyte(*cp++, LPMAXSPIN2, &sc->lp_dev))
+ goto end;
+ } while ((mm = mm->m_next));
+
+ err = 0; /* no errors were encountered */
+
+ end:
+ --cp;
+ ppb_wdtr(&sc->lp_dev, txmitl[*cp] ^ 0x17);
+
+ if (err) { /* if we didn't timeout... */
+ ifp->if_oerrors++;
+ lprintf("X");
+ } else {
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ /*
+ * We need to prepend the packet type as
+ * a two byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer to it).
+ */
+ struct mbuf m0;
+ u_short hdr = 0x800;
+
+ m0.m_next = m;
+ m0.m_len = 2;
+ m0.m_data = (char *)&hdr;
+
+ bpf_mtap(ifp, &m0);
+ }
+#endif
+ }
+
+ m_freem(m);
+
+ if (ppb_rstr(&sc->lp_dev) & LPIP_SHAKE) {
+ lprintf("^");
+ lpintr(ifp->if_unit);
+ }
+
+ (void) splx(s);
+ return 0;
+}
diff --git a/sys/dev/ppbus/nlpt.c b/sys/dev/ppbus/nlpt.c
index 2915806..e855fae 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.7 1998/01/24 02:54:05 eivind Exp $
+ * $Id: nlpt.c,v 1.8 1998/06/07 17:09:48 dfr Exp $
*/
/*
@@ -134,7 +134,7 @@ DATA_SET(ppbdriver_set, nlptdriver);
#define OBUSY (1<<3) /* printer is busy doing output */
#define LPTOUT (1<<4) /* timeout while not selected */
#define TOUT (1<<5) /* timeout while not selected */
-#define INIT (1<<6) /* waiting to initialize for open */
+#define LPTINIT (1<<6) /* waiting to initialize for open */
#define INTERRUPTED (1<<7) /* write call was interrupted */
#define HAVEBUS (1<<8) /* the driver owns the bus */
@@ -168,15 +168,25 @@ static struct cdevsw nlpt_cdevsw =
static int
lpt_request_ppbus(struct lpt_data *sc, int how)
{
- sc->sc_state |= HAVEBUS;
- return (ppb_request_bus(&sc->lpt_dev, how));
+ int error;
+
+ /* we have the bus only if the request succeded */
+ if ((error = ppb_request_bus(&sc->lpt_dev, how)) == 0)
+ sc->sc_state |= HAVEBUS;
+
+ return (error);
}
static int
lpt_release_ppbus(struct lpt_data *sc)
{
- sc->sc_state &= ~HAVEBUS;
- return (ppb_release_bus(&sc->lpt_dev));
+ int error;
+
+ /* we do not have the bus only if the request succeded */
+ if ((error = ppb_release_bus(&sc->lpt_dev)) == 0)
+ sc->sc_state &= ~HAVEBUS;
+
+ return (error);
}
/*
@@ -316,6 +326,7 @@ nlptprobe(struct ppb_data *ppb)
* ppbus dependent initialisation.
*/
sc->lpt_dev.id_unit = sc->lpt_unit;
+ sc->lpt_dev.name = nlptdriver.name;
sc->lpt_dev.ppb = ppb;
sc->lpt_dev.intr = nlptintr;
@@ -401,19 +412,6 @@ nlptout(void *arg)
* Avoid possible hangs do to missed interrupts
*/
if (sc->sc_xfercnt) {
- /* if we cannot allocate the bus NOW, retry later */
- if ((sc->sc_state & HAVEBUS) == 0 &&
- lpt_request_ppbus (sc, PPB_DONTWAIT)) {
-
- sc->sc_backoff++;
- if (sc->sc_backoff > hz/LPTOUTMAX)
- sc->sc_backoff =
- sc->sc_backoff > hz/LPTOUTMAX;
- timeout(nlptout, (caddr_t)sc,
- sc->sc_backoff);
- return;
- }
-
pl = spltty();
nlptintr(sc->lpt_unit);
splx(pl);
@@ -447,7 +445,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
nlprintf(LPT_NAME ": still open %x\n", sc->sc_state);
return(EBUSY);
} else
- sc->sc_state |= INIT;
+ sc->sc_state |= LPTINIT;
sc->sc_flags = LPTFLAGS(minor(dev));
@@ -457,7 +455,9 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
return(0);
}
- if (lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR))
+ /* 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);
s = spltty();
@@ -523,7 +523,7 @@ nlptopen(dev_t dev, int flags, int fmt, struct proc *p)
sc->sc_xfercnt = 0;
splx(s);
- /* release the bus, nlptout() will try to allocate it later */
+ /* release the ppbus */
lpt_release_ppbus(sc);
/* only use timeout if using interrupt */
@@ -572,9 +572,10 @@ nlptclose(dev_t dev, int flags, int fmt, struct proc *p)
ppb_wctr(&sc->lpt_dev, LPC_NINIT);
brelse(sc->sc_inbuf);
+end_close:
+ /* release the bus anyway */
lpt_release_ppbus(sc);
-end_close:
sc->sc_state = 0;
sc->sc_xfercnt = 0;
nlprintf("closed.\n");
@@ -660,7 +661,9 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag)
return(EPERM);
}
- if (lpt_request_ppbus(sc, PPB_WAIT|PPB_INTR))
+ /* 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);
sc->sc_state &= ~INTERRUPTED;
@@ -691,12 +694,15 @@ nlptwrite(dev_t dev, struct uio *uio, int ioflag)
nlprintf("p");
err = nlpt_pushbytes(sc);
- lpt_release_ppbus(sc);
if (err)
return(err);
}
}
+
+ /* we have not been interrupted, release the ppbus */
+ lpt_release_ppbus(sc);
+
return(0);
}
@@ -713,6 +719,10 @@ nlptintr(int unit)
struct lpt_data *sc = lptdata[unit];
int sts;
int i;
+
+ /* we must own the bus to use it */
+ if ((sc->sc_state & HAVEBUS) == 0)
+ return;
/*
* Is printer online and ready for output?
@@ -744,7 +754,6 @@ nlptintr(int unit)
* Wakeup is not done if write call was interrupted.
*/
sc->sc_state &= ~OBUSY;
- lpt_release_ppbus(sc);
if(!(sc->sc_state & INTERRUPTED))
wakeup((caddr_t)sc);
diff --git a/sys/dev/ppbus/ppb_1284.c b/sys/dev/ppbus/ppb_1284.c
index d1ab1c5..0a0ddeb 100644
--- a/sys/dev/ppbus/ppb_1284.c
+++ b/sys/dev/ppbus/ppb_1284.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ppb_1284.c,v 1.2 1997/09/01 00:51:44 bde Exp $
+ * $Id: ppb_1284.c,v 1.3 1998/01/31 07:23:06 eivind Exp $
*
*/
@@ -38,14 +38,23 @@
#include <dev/ppbus/ppb_1284.h>
/*
- * nibble_1284_wait()
+ * do_1284_wait()
*
* Wait for the peripherial up to 40ms
*/
int
-nibble_1284_wait(struct ppb_device *dev, char mask, char status)
+do_1284_wait(struct ppb_device *dev, char mask, char status)
{
int i;
+ char r;
+
+ /* try up to 5ms */
+ for (i = 0; i < 20; i++) {
+ r = ppb_rstr(dev);
+ DELAY(25);
+ if ((r & mask) == status)
+ return (0);
+ }
return (ppb_poll_device(dev, 4, mask, status, PPB_NOINTR));
}
@@ -53,26 +62,55 @@ nibble_1284_wait(struct ppb_device *dev, char mask, char status)
#define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4)
/*
+ * byte_1284_inbyte()
+ *
+ * Read 1 byte in BYTE mode
+ */
+int
+byte_1284_inbyte(struct ppb_device *dev, char *buffer)
+{
+ int error;
+
+ /* notify the peripherial to put data on the lines */
+ ppb_wctr(dev, PCD | AUTOFEED | nSTROBE | nINIT | nSELECTIN);
+
+ /* wait for valid byte signal */
+ if ((error = do_1284_wait(dev, nACK, 0)))
+ return (error);
+
+ /* fetch data */
+ *buffer = ppb_rdtr(dev);
+
+ /* indicate that data has been received, not ready for another */
+ ppb_wctr(dev, PCD | nAUTOFEED | nSTROBE | nINIT | nSELECTIN);
+
+ /* wait peripherial's acknowledgement */
+ if ((error = do_1284_wait(dev, nACK, nACK)))
+ return (error);
+
+ /* acknowledge the peripherial */
+ ppb_wctr(dev, PCD | nAUTOFEED | STROBE | nINIT | nSELECTIN);
+
+ return (0);
+}
+
+/*
* nibble_1284_inbyte()
*
- * Read data in NIBBLE mode
+ * Read 1 byte in NIBBLE mode
*/
int
nibble_1284_inbyte(struct ppb_device *dev, char *buffer)
{
- char nibble[2], r;
+ char nibble[2];
int i, error;
- r = ppb_rctr(dev);
-
for (i = 0; i < 2; i++) {
/* ready to take data (nAUTO low) */
- ppb_wctr(dev, r | AUTOFEED);
+ ppb_wctr(dev, AUTOFEED | nSTROBE | nINIT | nSELECTIN);
- if ((error = nibble_1284_wait(dev, nACK, 0))) {
- ppb_wctr(dev, r);
+ if ((error = do_1284_wait(dev, nACK, 0)))
return (error);
- }
/* read nibble */
nibble[i] = ppb_rstr(dev);
@@ -82,13 +120,11 @@ nibble_1284_inbyte(struct ppb_device *dev, char *buffer)
#endif
/* ack, not ready for another nibble */
- ppb_wctr(dev, r & ~AUTOFEED);
+ ppb_wctr(dev, nAUTOFEED | nSTROBE | nINIT | nSELECTIN);
/* wait ack from peripherial */
- if ((error = nibble_1284_wait(dev, nACK, nACK))) {
- ppb_wctr(dev, r);
+ if ((error = do_1284_wait(dev, nACK, nACK)))
return (error);
- }
}
*buffer = ((nibble2char(nibble[1]) << 4) & 0xf0) |
@@ -112,11 +148,11 @@ nibble_1284_sync(struct ppb_device *dev)
ctr = ppb_rctr(dev);
ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN);
- if (nibble_1284_wait(dev, nACK, 0))
+ if (do_1284_wait(dev, nACK, 0))
return;
ppb_wctr(dev, ctr | AUTOFEED);
- nibble_1284_wait(dev, nACK, nACK);
+ do_1284_wait(dev, nACK, nACK);
ppb_wctr(dev, (ctr & ~AUTOFEED) | SELECTIN);
@@ -140,7 +176,7 @@ nibble_1284_mode(struct ppb_device *dev, int mode)
DELAY(5);
ppb_wctr(dev, (ctrl & ~SELECTIN) | AUTOFEED);
- if ((error = nibble_1284_wait(dev, nACK | ERROR | SELECT | nFAULT,
+ if ((error = do_1284_wait(dev, nACK | ERROR | SELECT | nFAULT,
ERROR | SELECT | nFAULT))) {
ppb_wctr(dev, ctrl);
return (error);
diff --git a/sys/dev/ppbus/ppb_1284.h b/sys/dev/ppbus/ppb_1284.h
index 527d90a..980fe68 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$
+ * $Id: ppb_1284.h,v 1.1 1997/08/16 14:05:33 msmith Exp $
*
*/
#ifndef __1284_H
@@ -32,9 +32,12 @@
#define NIBBLE_1284_NORMAL 0
#define NIBBLE_1284_REQUEST_ID 4
-extern void nibble_1284_sync(struct ppb_device *);
+extern int do_1284_wait(struct ppb_device *, char, char);
+
+extern int byte_1284_inbyte(struct ppb_device *, char *);
+
extern int nibble_1284_inbyte(struct ppb_device *, char *);
-extern int nibble_1284_wait(struct ppb_device *, char, char);
+extern void nibble_1284_sync(struct ppb_device *);
extern int nibble_1284_mode(struct ppb_device *, int);
#endif
diff --git a/sys/dev/ppbus/ppb_base.c b/sys/dev/ppbus/ppb_base.c
index 8c956be..6bea760 100644
--- a/sys/dev/ppbus/ppb_base.c
+++ b/sys/dev/ppbus/ppb_base.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1997 Nicolas Souchu
+ * Copyright (c) 1997, 1998 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: ppb_base.c,v 1.2 1997/08/28 10:15:12 msmith Exp $
+ * $Id: ppb_base.c,v 1.3 1997/09/01 00:51:45 bde Exp $
*
*/
#include <sys/param.h>
@@ -98,78 +98,65 @@ ppb_poll_device(struct ppb_device *dev, int max,
}
/*
- * ppb_reset_epp_timeout()
+ * ppb_set_mode()
*
- * Reset the EPP timeout bit in the status register.
+ * Set the operating mode of the chipset
*/
int
-ppb_reset_epp_timeout(struct ppb_device *dev)
+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_owner != dev)
- return (EACCES);
+ if ((*ppb->ppb_link->adapter->setmode)(dev->id_unit, mode))
+ return (-1);
- (*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit);
+ /* XXX yet device mode = ppbus mode = chipset mode */
+ dev->mode = ppb->mode = mode;
- return (0);
+ return (old_mode);
}
/*
- * ppb_ecp_sync()
+ * ppb_reset_epp_timeout()
*
- * Wait for the ECP FIFO to be empty.
+ * Reset the EPP timeout bit in the status register
*/
int
-ppb_ecp_sync(struct ppb_device *dev)
+ppb_reset_epp_timeout(struct ppb_device *dev)
{
struct ppb_data *ppb = dev->ppb;
if (ppb->ppb_owner != dev)
return (EACCES);
- (*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit);
+ (*ppb->ppb_link->adapter->reset_epp_timeout)(dev->id_unit);
return (0);
}
/*
- * ppb_get_mode()
+ * ppb_ecp_sync()
*
- * Read the mode (SPP, EPP...) of the chipset.
+ * Wait for the ECP FIFO to be empty
*/
int
-ppb_get_mode(struct ppb_device *dev)
+ppb_ecp_sync(struct ppb_device *dev)
{
- return (dev->ppb->ppb_link->mode);
-}
+ struct ppb_data *ppb = dev->ppb;
-/*
- * ppb_get_epp_protocol()
- *
- * Read the EPP protocol (1.9 or 1.7).
- */
-int
-ppb_get_epp_protocol(struct ppb_device *dev)
-{
- return (dev->ppb->ppb_link->epp_protocol);
-}
+ if (ppb->ppb_owner != dev)
+ return (EACCES);
-/*
- * ppb_get_irq()
- *
- * Return the irq, 0 if none.
- */
-int
-ppb_get_irq(struct ppb_device *dev)
-{
- return (dev->ppb->ppb_link->id_irq);
+ (*ppb->ppb_link->adapter->ecp_sync)(dev->id_unit);
+
+ return (0);
}
/*
* ppb_get_status()
*
- * Read the status register and update the status info.
+ * Read the status register and update the status info
*/
int
ppb_get_status(struct ppb_device *dev, struct ppb_status *status)
diff --git a/sys/dev/ppbus/ppb_msq.c b/sys/dev/ppbus/ppb_msq.c
new file mode 100644
index 0000000..fff70a7
--- /dev/null
+++ b/sys/dev/ppbus/ppb_msq.c
@@ -0,0 +1,326 @@
+/*-
+ * Copyright (c) 1998 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ppb_msq.c,v 1.1.2.4 1998/06/16 23:35:51 son Exp $
+ *
+ */
+#include <machine/stdarg.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+
+/* msq index (see PPB_MAX_XFER)
+ * These are device modes
+ */
+#define COMPAT_MSQ 0x0
+#define NIBBLE_MSQ 0x1
+#define PS2_MSQ 0x2
+#define EPP17_MSQ 0x3
+#define EPP19_MSQ 0x4
+#define ECP_MSQ 0x5
+
+/*
+ * Device mode to submsq conversion
+ */
+static struct ppb_xfer *
+mode2xfer(struct ppb_device *dev, int opcode)
+{
+ int index, epp;
+ struct ppb_xfer *table;
+
+ switch (opcode) {
+ case MS_OP_GET:
+ table = dev->get_xfer;
+ break;
+
+ case MS_OP_PUT:
+ table = dev->put_xfer;
+ break;
+
+ default:
+ panic("%s: unknown opcode (%d)", __FUNCTION__, opcode);
+ }
+
+ /* retrieve the device operating mode */
+ switch (ppb_get_mode(dev)) {
+ case PPB_COMPATIBLE:
+ index = COMPAT_MSQ;
+ break;
+ case PPB_NIBBLE:
+ index = NIBBLE_MSQ;
+ break;
+ case PPB_PS2:
+ index = PS2_MSQ;
+ break;
+ case PPB_EPP:
+ switch ((epp = ppb_get_epp_protocol(dev))) {
+ case EPP_1_7:
+ index = EPP17_MSQ;
+ break;
+ case EPP_1_9:
+ index = EPP19_MSQ;
+ break;
+ default:
+ panic("%s: unknown EPP protocol (0x%x)!", __FUNCTION__,
+ epp);
+ }
+ break;
+ case PPB_ECP:
+ index = ECP_MSQ;
+ break;
+ default:
+ panic("%s: unknown mode (%d)", __FUNCTION__, dev->mode);
+ }
+
+ return (&table[index]);
+}
+
+/*
+ * ppb_MS_init()
+ *
+ * Initialize device dependent submicrosequence of the current mode
+ *
+ */
+int
+ppb_MS_init(struct ppb_device *dev, struct ppb_microseq *loop, int opcode)
+{
+ struct ppb_xfer *xfer = mode2xfer(dev, opcode);
+
+ xfer->loop = loop;
+
+ return (0);
+}
+
+/*
+ * ppb_MS_exec()
+ *
+ * Execute any microsequence opcode - expensive
+ *
+ */
+int
+ppb_MS_exec(struct ppb_device *dev, int opcode, union ppb_insarg param1,
+ union ppb_insarg param2, union ppb_insarg param3, int *ret)
+{
+ struct ppb_microseq msq[] = {
+ { MS_UNKNOWN, { MS_UNKNOWN, MS_UNKNOWN, MS_UNKNOWN } },
+ MS_RET(0)
+ };
+
+ /* initialize the corresponding microseq */
+ msq[0].opcode = opcode;
+ msq[0].arg[0] = param1;
+ msq[0].arg[1] = param2;
+ msq[0].arg[2] = param3;
+
+ /* execute the microseq */
+ return (ppb_MS_microseq(dev, msq, ret));
+}
+
+/*
+ * ppb_MS_loop()
+ *
+ * Execute a microseq loop
+ *
+ */
+int
+ppb_MS_loop(struct ppb_device *dev, struct ppb_microseq *prolog,
+ struct ppb_microseq *body, struct ppb_microseq *epilog,
+ int iter, int *ret)
+{
+ struct ppb_microseq loop_microseq[] = {
+ MS_CALL(NULL), /* execute prolog */
+
+ MS_SET(MS_UNKNOWN), /* set size of transfer */
+ /* loop: */
+ MS_CALL(NULL), /* execute body */
+ MS_DBRA(-1 /* loop: */),
+
+ MS_CALL(NULL), /* execute epilog */
+ MS_RET(0)
+ };
+
+ /* initialize the structure */
+ loop_microseq[0].arg[0].p = (void *)prolog;
+ loop_microseq[1].arg[0].i = iter;
+ loop_microseq[2].arg[0].p = (void *)body;
+ loop_microseq[4].arg[0].p = (void *)epilog;
+
+ /* execute the loop */
+ return (ppb_MS_microseq(dev, loop_microseq, ret));
+}
+
+/*
+ * ppb_MS_init_msq()
+ *
+ * Initialize a microsequence - see macros in ppb_msq.h
+ *
+ */
+int
+ppb_MS_init_msq(struct ppb_microseq *msq, int nbparam, ...)
+{
+ int i;
+ int param, ins, arg, type;
+ va_list p_list = 0;
+
+ va_start(p_list, nbparam);
+
+ for (i=0; i<nbparam; i++) {
+ /* retrieve the parameter descriptor */
+ param = va_arg(p_list, int);
+
+ ins = MS_INS(param);
+ arg = MS_ARG(param);
+ type = MS_TYP(param);
+
+ /* check the instruction position */
+ if (arg >= PPB_MS_MAXARGS)
+ panic("%s: parameter out of range (0x%x)!",
+ __FUNCTION__, param);
+
+#if 0
+ printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
+ __FUNCTION__, param, ins, arg, type);
+#endif
+
+ /* properly cast the parameter */
+ switch (type) {
+ case MS_TYP_INT:
+ msq[ins].arg[arg].i = va_arg(p_list, int);
+ break;
+
+ case MS_TYP_CHA:
+ msq[ins].arg[arg].c = va_arg(p_list, char);
+ break;
+
+ case MS_TYP_PTR:
+ msq[ins].arg[arg].p = va_arg(p_list, void *);
+ break;
+
+ case MS_TYP_FUN:
+ msq[ins].arg[arg].f = va_arg(p_list, void *);
+ break;
+
+ default:
+ panic("%s: unknown parameter (0x%x)!", __FUNCTION__,
+ param);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * ppb_MS_microseq()
+ *
+ * Interprete a microsequence. Some microinstructions are executed at adapter
+ * level to avoid function call overhead between ppbus and the adapter
+ */
+int
+ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret)
+{
+ struct ppb_data *ppb = dev->ppb;
+ struct ppb_microseq *mi; /* current microinstruction */
+ int msq_index;
+ int pc, error;
+
+ struct ppb_xfer *xfer;
+
+ /* microsequence executed to initialize the transfer */
+ struct ppb_microseq initxfer[] = {
+ MS_PTR(MS_UNKNOWN), /* set ptr to buffer */
+ MS_SET(MS_UNKNOWN), /* set transfer size */
+ MS_RET(0)
+ };
+
+ if (ppb->ppb_owner != dev)
+ return (EACCES);
+
+#define INCR_PC (pc ++)
+
+ pc = 0;
+ for (;;) {
+
+ /* retrieve the next microinstruction to execute */
+ mi = &msq[pc];
+
+ switch (mi->opcode) {
+ case MS_OP_PUT:
+ case MS_OP_GET:
+
+ /* attempt to choose the best mode for the device */
+ xfer = mode2xfer(dev, mi->opcode);
+
+ /* figure out if we should use ieee1284 code */
+ if (!xfer->loop)
+ panic("%s: IEEE1284 code not supported",
+ __FUNCTION__);
+
+ /* XXX should use ppb_MS_init_msq() */
+ initxfer[0].arg[0].p = mi->arg[0].p;
+ initxfer[1].arg[0].i = mi->arg[1].i;
+
+ /* initialize transfer */
+ ppb_MS_microseq(dev, initxfer, &error);
+
+ if (error)
+ goto error;
+
+ /* the xfer microsequence should not contain any
+ * MS_OP_PUT or MS_OP_GET!
+ */
+ ppb_MS_microseq(dev, xfer->loop, &error);
+
+ if (error)
+ goto error;
+
+ INCR_PC;
+ break;
+
+ case MS_OP_RET:
+ *ret = mi->arg[0].i; /* return code */
+ return (0);
+ break;
+
+ default:
+ /* executing microinstructions at ppc level is
+ * faster. This is the default if the microinstr
+ * is unknown here
+ */
+ if ((error = ppb->ppb_link->adapter->exec_microseq(
+ dev->id_unit, msq, &pc)))
+ return (error);
+
+ break;
+ }
+ }
+error:
+ *ret = error;
+ return (0);
+}
+
diff --git a/sys/dev/ppbus/ppb_msq.h b/sys/dev/ppbus/ppb_msq.h
new file mode 100644
index 0000000..71e41b2
--- /dev/null
+++ b/sys/dev/ppbus/ppb_msq.h
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 1998 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ppb_msq.h,v 1.1.2.6 1998/06/17 00:37:12 son Exp $
+ *
+ */
+#ifndef __PPB_MSQ_H
+#define __PPB_MSQ_H
+
+/*
+ * Basic definitions
+ */
+
+/* microsequence parameter descriptor */
+#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position */
+#define MS_ARG_MASK 0x0f00 /* mask to retrieve the argument number */
+#define MS_TYP_MASK 0xf000 /* mask to retrieve the type of the param */
+
+/* offset of each mask (see above) */
+#define MS_INS_OFFSET 0
+#define MS_ARG_OFFSET 8
+#define MS_TYP_OFFSET 12
+
+/* list of parameter types */
+#define MS_TYP_INT 0x0 /* integer */
+#define MS_TYP_CHA 0x1 /* character */
+#define MS_TYP_PTR 0x2 /* void pointer */
+#define MS_TYP_FUN 0x3 /* function pointer */
+
+#define MS_PARAM(ins,arg,typ) \
+ (((ins<<MS_INS_OFFSET) & MS_INS_MASK) | \
+ ((arg<<MS_ARG_OFFSET) & MS_ARG_MASK) | \
+ ((typ<<MS_TYP_OFFSET) & MS_TYP_MASK))
+
+#define MS_INS(param) ((param & MS_INS_MASK) >> MS_INS_OFFSET)
+#define MS_ARG(param) ((param & MS_ARG_MASK) >> MS_ARG_OFFSET)
+#define MS_TYP(param) ((param & MS_TYP_MASK) >> MS_TYP_OFFSET)
+
+/* microsequence opcodes - do not change! */
+#define MS_OP_GET 0 /* get <ptr>, <len> */
+#define MS_OP_PUT 1 /* put <ptr>, <len> */
+
+#define MS_OP_RFETCH 2 /* rfetch <reg>, <mask>, <ptr> */
+#define MS_OP_RSET 3 /* rset <reg>, <mask>, <mask> */
+#define MS_OP_RASSERT 4 /* rassert <reg>, <mask> */
+#define MS_OP_DELAY 5 /* delay <val> */
+#define MS_OP_SET 6 /* set <val> */
+#define MS_OP_DBRA 7 /* dbra <offset> */
+#define MS_OP_BRSET 8 /* brset <mask>, <offset> */
+#define MS_OP_BRCLEAR 9 /* brclear <mask>, <offset> */
+#define MS_OP_RET 10 /* ret <retcode> */
+#define MS_OP_C_CALL 11 /* c_call <function>, <parameter> */
+#define MS_OP_PTR 12 /* ptr <pointer> */
+#define MS_RESERVED_1 13 /* reserved */
+#define MS_RESERVED_2 14 /* reserved */
+#define MS_OP_SUBRET 15 /* subret <code> */
+#define MS_OP_CALL 16 /* call <microsequence> */
+#define MS_OP_RASSERT_P 17 /* rassert_p <iter>, <reg> */
+#define MS_OP_RFETCH_P 18 /* rfetch_p <iter>, <reg>, <mask> */
+#define MS_OP_TRIG 19 /* trigger <reg>, <len>, <array> */
+
+/* common masks */
+#define MS_CLEAR_ALL 0x0
+#define MS_ASSERT_NONE 0x0
+#define MS_ASSERT_ALL 0xff
+#define MS_FETCH_ALL 0xff
+
+/* undefined parameter value */
+#define MS_UNKNOWN 0
+
+/* these are register numbers according to our PC-like parallel port model */
+#define MS_REG_DTR 0x0
+#define MS_REG_STR 0x1
+#define MS_REG_CTR 0x2
+#define MS_REG_EPP 0x4
+
+/*
+ * Microsequence macro abstraction level
+ */
+
+/* register operations */
+#define MS_RSET(reg,assert,clear) { MS_OP_RSET, { reg, assert, clear} }
+#define MS_RASSERT(reg,byte) { MS_OP_RASSERT, { reg, byte } }
+#define MS_RCLR(reg,clear) { MS_OP_RSET, { reg, MS_ASSERT_NONE, clear } }
+
+#define MS_RFETCH(reg,mask,ptr) { MS_OP_RFETCH, { reg, mask, ptr } }
+
+/* trigger the port with array[char, delay,...] */
+#define MS_TRIG(reg,len,array) { MS_OP_TRIG, { reg, len, array } }
+
+/* assert/fetch from/to ptr */
+#define MS_RASSERT_P(n,reg) { MS_OP_RASSERT_P, { n, reg } }
+#define MS_RFETCH_P(n,reg,mask) { MS_OP_RFETCH_P, { n, reg, mask } }
+
+/* ptr manipulation */
+#define MS_PTR(ptr) { MS_OP_PTR, { ptr } }
+
+#define MS_DASS(byte) MS_RASSERT(MS_REG_DTR,byte)
+#define MS_SASS(byte) MS_RASSERT(MS_REG_STR,byte)
+#define MS_CASS(byte) MS_RASSERT(MS_REG_CTR,byte)
+
+#define MS_SET(offset) { MS_OP_SET, { offset } }
+#define MS_BRSET(mask,offset) { MS_OP_BRSET, { mask, offset } }
+#define MS_DBRA(offset) { MS_OP_DBRA, { offset } }
+
+/* C function or submicrosequence call */
+#define MS_C_CALL(function,parameter) \
+ { MS_OP_C_CALL, { function, parameter } }
+#define MS_CALL(microseq) { MS_OP_CALL, { microseq } }
+
+/* mode dependent read/write operations
+ * ppb_MS_xxx_init() call required otherwise default is
+ * IEEE1284 operating mode */
+#define MS_PUT(ptr,len) { MS_OP_PUT, { ptr, len } }
+#define MS_GET(ptr,len) { MS_OP_GET, { ptr, len } }
+
+/* delay in microseconds */
+#define MS_DELAY(delay) { MS_OP_DELAY, { delay } }
+
+/* return from submicrosequence execution or microseqence execution */
+#define MS_SUBRET(code) { MS_OP_SUBRET, { code } }
+#define MS_RET(code) { MS_OP_RET, { code } }
+
+/*
+ * Function abstraction level
+ */
+
+#define ppb_MS_GET_init(dev,body) ppb_MS_init(dev, body, MS_OP_GET)
+
+#define ppb_MS_PUT_init(dev,body) ppb_MS_init(dev, body, MS_OP_PUT)
+
+extern int ppb_MS_init(
+ struct ppb_device *, /* ppbus device */
+ struct ppb_microseq *, /* loop msq to assign */
+ int opcode /* MS_OP_GET, MS_OP_PUT */
+ );
+
+extern int ppb_MS_init_msq(
+ struct ppb_microseq *,
+ int, /* number of parameters */
+ ... /* descriptor, value, ... */
+ );
+
+extern int ppb_MS_exec(
+ struct ppb_device *, /* ppbus device */
+ int, /* microseq opcode */
+ union ppb_insarg, /* param1 */
+ union ppb_insarg, /* param2 */
+ union ppb_insarg, /* param3 */
+ int * /* returned value */
+ );
+
+extern int ppb_MS_loop(
+ struct ppb_device *, /* ppbus device */
+ struct ppb_microseq *, /* prologue msq of loop */
+ struct ppb_microseq *, /* body msq of loop */
+ struct ppb_microseq *, /* epilogue msq of loop */
+ int, /* number of iter */
+ int * /* returned value */
+ );
+
+extern int ppb_MS_microseq(
+ struct ppb_device *, /* ppbus device */
+ struct ppb_microseq *, /* msq to execute */
+ int * /* returned value */
+ );
+
+#endif
diff --git a/sys/dev/ppbus/ppbconf.c b/sys/dev/ppbus/ppbconf.c
index 647676d..5756327 100644
--- a/sys/dev/ppbus/ppbconf.c
+++ b/sys/dev/ppbus/ppbconf.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1997 Nicolas Souchu
+ * Copyright (c) 1997, 1998 Nicolas Souchu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,7 +37,7 @@
#include <dev/ppbus/ppbconf.h>
#include <dev/ppbus/ppb_1284.h>
-static LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */
+LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */
/*
* Add a null driver so that the linker set always exists.
@@ -53,7 +53,6 @@ DATA_SET(ppbdriver_set, nulldriver);
* ppb_alloc_bus()
*
* Allocate area to store the ppbus description.
- * This function is called by ppcattach().
*/
struct ppb_data *
ppb_alloc_bus(void)
@@ -137,8 +136,8 @@ ppb_pnp_detect(struct ppb_data *ppb)
{
char *token, *q, *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 */
@@ -146,44 +145,48 @@ ppb_pnp_detect(struct ppb_data *ppb)
pnpdev.ppb = ppb;
-#ifdef PnP_DEBUG
- printf("ppb: <PnP> probing PnP devices on ppbus%d...\n",
- ppb->ppb_link->adapter_unit);
-#endif
+ if (bootverbose)
+ printf("ppb: <PnP> probing 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);
+ }
ppb_wctr(&pnpdev, nINIT | SELECTIN);
/* select NIBBLE_1284_REQUEST_ID mode */
if ((error = nibble_1284_mode(&pnpdev, NIBBLE_1284_REQUEST_ID))) {
-#ifdef PnP_DEBUG
- printf("ppb: <PnP> nibble_1284_mode()=%d\n", error);
-#endif
- return (-1);
+ if (bootverbose)
+ printf("ppb: <PnP> nibble_1284_mode()=%d\n", error);
+ goto end_detect;
}
len = 0;
for (q = str; !(ppb_rstr(&pnpdev) & ERROR); q++) {
if ((error = nibble_1284_inbyte(&pnpdev, q))) {
-#ifdef PnP_DEBUG
- printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", error);
-#endif
- return (-1);
+ if (bootverbose)
+ 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");
- return (-1);
+ goto end_detect;
}
}
*q = '\0';
nibble_1284_sync(&pnpdev);
-#ifdef PnP_DEBUG
- printf("ppb: <PnP> %d characters: ", len);
- for (i = 0; i < len; i++)
- printf("0x%x ", str[i]);
- printf("\n");
-#endif
+ if (bootverbose) {
+ printf("ppb: <PnP> %d characters: ", len);
+ for (i = 0; i < len; i++)
+ printf("0x%x ", str[i]);
+ printf("\n");
+ }
/* replace ';' characters by '\0' */
for (i = 0; i < len; i++)
@@ -226,12 +229,16 @@ ppb_pnp_detect(struct ppb_data *ppb)
/* identify class ident */
for (i = 0; pnp_tokens[i] != NULL; i++) {
if (search_token(class, len, pnp_tokens[i]) != NULL) {
- return (i);
- break;
+ class_id = i;
+ goto end_detect;
}
}
- return (PPB_PnP_UNKNOWN);
+ class_id = PPB_PnP_UNKNOWN;
+
+end_detect:
+ ppb_release_bus(&pnpdev);
+ return (class_id);
}
/*
@@ -305,6 +312,24 @@ ppb_lookup_bus(int base_port)
}
/*
+ * ppb_lookup_link()
+ *
+ * Get ppb_data structure pointer according to the unit value
+ * of the corresponding link structure
+ */
+struct ppb_data *
+ppb_lookup_link(int unit)
+{
+ struct ppb_data *ppb;
+
+ for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
+ if (ppb->ppb_link->adapter_unit == unit)
+ break;
+
+ return (ppb);
+}
+
+/*
* ppb_attach_device()
*
* Called by loadable kernel modules to add a device
@@ -370,6 +395,15 @@ ppb_request_bus(struct ppb_device *dev, int how)
} else {
ppb->ppb_owner = dev;
+ /* restore the context of the device
+ * The first time, ctx.valid is certainly false
+ * then do not change anything. This is usefull for
+ * drivers that do not set there operating mode
+ * during attachement
+ */
+ if (dev->ctx.valid)
+ ppb_set_mode(dev, dev->ctx.mode);
+
splx(s);
return (0);
}
@@ -398,6 +432,12 @@ ppb_release_bus(struct ppb_device *dev)
ppb->ppb_owner = 0;
splx(s);
+ /* save the context of the device */
+ dev->ctx.mode = ppb_get_mode(dev);
+
+ /* ok, now the context of the device is valid */
+ dev->ctx.valid = 1;
+
/* wakeup waiting processes */
wakeup(ppb);
diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h
index 5a69d85..fc723d4 100644
--- a/sys/dev/ppbus/ppbconf.h
+++ b/sys/dev/ppbus/ppbconf.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1997 Nicolas Souchu
+ * Copyright (c) 1997, 1998 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.h,v 1.5 1997/09/01 18:39:37 bde Exp $
+ * $Id: ppbconf.h,v 1.6 1998/06/07 19:44:21 phk Exp $
*
*/
#ifndef __PPBCONF_H
@@ -37,21 +37,23 @@
#define PPBPRI PZERO+8
/*
- * Parallel Port Chipset modes.
+ * Parallel Port Chipset mode masks.
+ * NIBBLE mode is supposed to be available under each other modes.
*/
-#define PPB_AUTODETECT 0x0 /* autodetect */
-#define PPB_NIBBLE 0x1 /* standard 4 bit mode */
+#define PPB_COMPATIBLE 0x0 /* Centronics compatible mode */
+
+#define PPB_NIBBLE 0x1 /* reverse 4 bit mode */
#define PPB_PS2 0x2 /* PS/2 byte mode */
-#define PPB_EPP 0x3 /* EPP mode, 32 bit */
-#define PPB_ECP_EPP 0x4 /* ECP in EPP mode */
-#define PPB_ECP_PS2 0x5 /* ECP in PS/2 mode */
-#define PPB_ECP 0x6 /* ECP mode */
-#define PPB_UNKNOWN 0x7 /* the last one */
+#define PPB_EPP 0x4 /* EPP mode, 32 bit */
+#define PPB_ECP 0x8 /* ECP mode */
-#define PPB_IS_EPP(mode) (mode == PPB_EPP || mode == PPB_ECP_EPP)
+#define PPB_SPP PPB_NIBBLE|PPB_PS2
+#define PPB_IS_EPP(mode) (mode & PPB_EPP)
#define PPB_IN_EPP_MODE(dev) (PPB_IS_EPP (ppb_get_mode (dev)))
+#define n(flags) (~(flags) & (flags))
+
/*
* Parallel Port Chipset control bits.
*/
@@ -62,6 +64,12 @@
#define IRQENABLE 0x10
#define PCD 0x20
+#define nSTROBE n(STROBE)
+#define nAUTOFEED n(AUTOFEED)
+#define INIT n(nINIT)
+#define nSELECTIN n(SELECTIN)
+#define nPCD n(PCD)
+
/*
* Parallel Port Chipset status bits.
*/
@@ -87,21 +95,74 @@ struct ppb_status {
};
/*
- * How tsleep () is called in ppb_request_bus ().
+ * How tsleep() is called in ppb_request_bus().
*/
#define PPB_DONTWAIT 0
#define PPB_NOINTR 0
#define PPB_WAIT 0x1
#define PPB_INTR 0x2
-struct ppb_data; /* see below */
+/*
+ * Microsequence stuff.
+ */
+#define PPB_MS_MAXLEN 64 /* XXX according to MS_INS_MASK */
+#define PPB_MS_MAXARGS 3 /* according to MS_ARG_MASK */
+
+/* maximum number of mode dependent
+ * submicrosequences for in/out operations
+ */
+#define PPB_MAX_XFER 6
+
+union ppb_insarg {
+ int i;
+ char c;
+ void *p;
+ int (* f)(void *, char *);
+};
+
+struct ppb_microseq {
+ int opcode; /* microins. opcode */
+ union ppb_insarg arg[PPB_MS_MAXARGS]; /* arguments */
+};
+
+/* microseqences used for GET/PUT operations */
+struct ppb_xfer {
+ struct ppb_microseq *loop; /* the loop microsequence */
+};
/*
* Parallel Port Bus Device structure.
*/
+struct ppb_data; /* see below */
+
+struct ppb_context {
+ int valid; /* 1 if the struct is valid */
+ int mode; /* XXX chipset operating mode */
+
+ struct microseq *curpc; /* pc in curmsq */
+ 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 */
+
+ struct ppb_context ctx; /* context of the device */
+
+ /* mode dependent get msq. If NULL,
+ * IEEE1284 code is used */
+ struct ppb_xfer
+ get_xfer[PPB_MAX_XFER];
+
+ /* mode dependent put msq. If NULL,
+ * IEEE1284 code is used */
+ struct ppb_xfer
+ put_xfer[PPB_MAX_XFER];
void (*intr)(int); /* interrupt handler */
@@ -119,6 +180,10 @@ struct ppb_adapter {
void (*reset_epp_timeout)(int);
void (*ecp_sync)(int);
+ int (*exec_microseq)(int, struct ppb_microseq *, int *);
+
+ int (*setmode)(int, int);
+
void (*outsb_epp)(int, char *, int);
void (*outsw_epp)(int, char *, int);
void (*outsl_epp)(int, char *, int);
@@ -147,10 +212,8 @@ struct ppb_adapter {
struct ppb_link {
int adapter_unit; /* unit of the adapter */
-
int base; /* base address of the port */
int id_irq; /* != 0 if irq enabled */
- int mode; /* NIBBLE, PS2, EPP, ECP */
#define EPP_1_9 0x0 /* default */
#define EPP_1_7 0x1
@@ -164,7 +227,7 @@ struct ppb_link {
/*
* Maximum size of the PnP info string
*/
-#define PPB_PnP_STRING_SIZE 160 /* XXX */
+#define PPB_PnP_STRING_SIZE 128 /* XXX */
/*
* Parallel Port Bus structure.
@@ -184,6 +247,11 @@ struct ppb_data {
#define PPB_PnP_UNKNOWN 10
int class_id; /* not a PnP device if class_id < 0 */
+ 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 */
LIST_HEAD(, ppb_device) ppb_devs; /* list of devices on the bus */
@@ -205,6 +273,7 @@ extern struct linker_set ppbdriver_set;
extern struct ppb_data *ppb_alloc_bus(void);
extern struct ppb_data *ppb_next_bus(struct ppb_data *);
extern struct ppb_data *ppb_lookup_bus(int);
+extern struct ppb_data *ppb_lookup_link(int);
extern int ppb_attach_device(struct ppb_device *);
extern void ppb_remove_device(struct ppb_device *);
@@ -220,56 +289,61 @@ extern int ppb_poll_device(struct ppb_device *, int, char, char, int);
extern int ppb_reset_epp_timeout(struct ppb_device *);
extern int ppb_ecp_sync(struct ppb_device *);
extern int ppb_get_status(struct ppb_device *, struct ppb_status *);
-extern int ppb_get_mode(struct ppb_device *);
-extern int ppb_get_epp_protocol(struct ppb_device *);
-extern int ppb_get_irq(struct ppb_device *);
+
+extern int ppb_set_mode(struct ppb_device *, int);
/*
* These are defined as macros for speedup.
*/
-#define ppb_outsb_epp(dev,buf,cnt) \
- (*(dev)->ppb->ppb_link->adapter->outsb_epp) \
+#define ppb_get_base_addr(dev) ((dev)->ppb->ppb_link->base)
+#define ppb_get_epp_protocol(dev) ((dev)->ppb->ppb_link->epp_protocol)
+#define ppb_get_irq(dev) ((dev)->ppb->ppb_link->id_irq)
+
+#define ppb_get_mode(dev) ((dev)->mode)
+
+#define ppb_outsb_epp(dev,buf,cnt) \
+ (*(dev)->ppb->ppb_link->adapter->outsb_epp) \
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
-#define ppb_outsw_epp(dev,buf,cnt) \
- (*(dev)->ppb->ppb_link->adapter->outsw_epp) \
+#define ppb_outsw_epp(dev,buf,cnt) \
+ (*(dev)->ppb->ppb_link->adapter->outsw_epp) \
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
-#define ppb_outsl_epp(dev,buf,cnt) \
- (*(dev)->ppb->ppb_link->adapter->outsl_epp) \
+#define ppb_outsl_epp(dev,buf,cnt) \
+ (*(dev)->ppb->ppb_link->adapter->outsl_epp) \
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
-#define ppb_insb_epp(dev,buf,cnt) \
- (*(dev)->ppb->ppb_link->adapter->insb_epp) \
+#define ppb_insb_epp(dev,buf,cnt) \
+ (*(dev)->ppb->ppb_link->adapter->insb_epp) \
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
-#define ppb_insw_epp(dev,buf,cnt) \
- (*(dev)->ppb->ppb_link->adapter->insw_epp) \
+#define ppb_insw_epp(dev,buf,cnt) \
+ (*(dev)->ppb->ppb_link->adapter->insw_epp) \
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
-#define ppb_insl_epp(dev,buf,cnt) \
- (*(dev)->ppb->ppb_link->adapter->insl_epp) \
+#define ppb_insl_epp(dev,buf,cnt) \
+ (*(dev)->ppb->ppb_link->adapter->insl_epp) \
((dev)->ppb->ppb_link->adapter_unit, buf, cnt)
-#define ppb_rdtr(dev) (*(dev)->ppb->ppb_link->adapter->r_dtr) \
- ((dev)->ppb->ppb_link->adapter_unit)
-#define ppb_rstr(dev) (*(dev)->ppb->ppb_link->adapter->r_str) \
- ((dev)->ppb->ppb_link->adapter_unit)
-#define ppb_rctr(dev) (*(dev)->ppb->ppb_link->adapter->r_ctr) \
+#define ppb_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \
((dev)->ppb->ppb_link->adapter_unit)
-#define ppb_repp(dev) (*(dev)->ppb->ppb_link->adapter->r_epp) \
+#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \
((dev)->ppb->ppb_link->adapter_unit)
-#define ppb_recr(dev) (*(dev)->ppb->ppb_link->adapter->r_ecr) \
+#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \
((dev)->ppb->ppb_link->adapter_unit)
-#define ppb_rfifo(dev) (*(dev)->ppb->ppb_link->adapter->r_fifo) \
- ((dev)->ppb->ppb_link->adapter_unit)
-
-#define ppb_wdtr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_dtr) \
+#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \
((dev)->ppb->ppb_link->adapter_unit, byte)
-#define ppb_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \
+#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \
((dev)->ppb->ppb_link->adapter_unit, byte)
-#define ppb_wctr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ctr) \
+#define ppb_wfifo(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_fifo) \
((dev)->ppb->ppb_link->adapter_unit, byte)
-#define ppb_wepp(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_epp) \
+
+#define ppb_rdtr(dev) (*(dev)->ppb->ppb_link->adapter->r_dtr) \
+ ((dev)->ppb->ppb_link->adapter_unit)
+#define ppb_rstr(dev) (*(dev)->ppb->ppb_link->adapter->r_str) \
+ ((dev)->ppb->ppb_link->adapter_unit)
+#define ppb_rctr(dev) (*(dev)->ppb->ppb_link->adapter->r_ctr) \
+ ((dev)->ppb->ppb_link->adapter_unit)
+#define ppb_wdtr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_dtr) \
((dev)->ppb->ppb_link->adapter_unit, byte)
-#define ppb_wecr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ecr) \
+#define ppb_wstr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_str) \
((dev)->ppb->ppb_link->adapter_unit, byte)
-#define ppb_wfifo(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_fifo) \
+#define ppb_wctr(dev,byte) (*(dev)->ppb->ppb_link->adapter->w_ctr) \
((dev)->ppb->ppb_link->adapter_unit, byte)
#endif
diff --git a/sys/dev/ppbus/pps.c b/sys/dev/ppbus/pps.c
index fa975d2..e39eb14 100644
--- a/sys/dev/ppbus/pps.c
+++ b/sys/dev/ppbus/pps.c
@@ -6,7 +6,7 @@
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
- * $Id: pps.c,v 1.8 1998/06/13 09:30:10 phk Exp $
+ * $Id: pps.c,v 1.9 1998/06/21 18:02:32 bde Exp $
*
* This driver implements a draft-mogul-pps-api-02.txt PPS source.
*
@@ -93,6 +93,7 @@ ppsprobe(struct ppb_data *ppb)
sc->pps_dev.id_unit = sc->pps_unit;
sc->pps_dev.ppb = ppb;
+ sc->pps_dev.name = ppsdriver.name;
sc->pps_dev.intr = ppsintr;
return (&sc->pps_dev);
diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c
index 44cb54d..aa5779a 100644
--- a/sys/dev/ppbus/vpo.c
+++ b/sys/dev/ppbus/vpo.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1997 Nicolas Souchu
+ * Copyright (c) 1997, 1998 Nicolas Souchu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,39 +44,42 @@
#endif /*KERNEL */
#include <dev/ppbus/ppbconf.h>
-#include <dev/ppbus/vpo.h>
+#include <dev/ppbus/vpoio.h>
-/* --------------------------------------------------------------------
- * HERE ARE THINGS YOU MAY HAVE/WANT TO CHANGE
- */
+#define VP0_BUFFER_SIZE 0x12000
-/*
- * XXX
- * We may add a timeout queue to avoid active polling on nACK.
- */
-#define VP0_SELTMO 5000 /* select timeout */
-#define VP0_FAST_SPINTMO 500000 /* wait status timeout */
-#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */
+struct vpo_sense {
+ struct scsi_sense cmd;
+ unsigned int stat;
+ unsigned int count;
+};
-/*
- * DO NOT MODIFY ANYTHING UNDER THIS LINE
- * --------------------------------------------------------------------
- */
+struct vpo_data {
+ unsigned short vpo_unit;
+
+ int vpo_stat;
+ int vpo_count;
+ int vpo_error;
+
+ struct ppb_status vpo_status;
+ struct vpo_sense vpo_sense;
+
+ unsigned char vpo_buffer[VP0_BUFFER_SIZE];
+
+ struct vpoio_data vpo_io; /* interface to low level functions */
+
+ struct scsi_link sc_link;
+};
-static __inline int vpoio_do_scsi(struct vpo_data *, int, int, char *, int,
- char *, int, int *, int *);
static int32_t vpo_scsi_cmd(struct scsi_xfer *);
static void vpominphys(struct buf *);
static u_int32_t vpo_adapter_info(int);
-static int vpo_detect(struct vpo_data *vpo);
-
static int nvpo = 0;
#define MAXVP0 8 /* XXX not much better! */
static struct vpo_data *vpodata[MAXVP0];
-#ifdef KERNEL
static struct scsi_adapter vpo_switch =
{
vpo_scsi_cmd,
@@ -117,8 +120,6 @@ static struct ppb_driver vpodriver = {
DATA_SET(ppbdriver_set, vpodriver);
-#endif /* KERNEL */
-
static u_int32_t
vpo_adapter_info(int unit)
{
@@ -134,8 +135,8 @@ vpo_adapter_info(int unit)
static struct ppb_device *
vpoprobe(struct ppb_data *ppb)
{
-
struct vpo_data *vpo;
+ struct ppb_device *dev;
if (nvpo >= MAXVP0) {
printf("vpo: Too many devices (max %d)\n", MAXVP0);
@@ -155,20 +156,18 @@ vpoprobe(struct ppb_data *ppb)
/* vpo dependent initialisation */
vpo->vpo_unit = nvpo;
- /* ppbus dependent initialisation */
- vpo->vpo_dev.id_unit = vpo->vpo_unit;
- vpo->vpo_dev.ppb = ppb;
+ /* ok, go to next device on next probe */
+ nvpo ++;
+
+ /* low level probe */
+ vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit);
- /* now, try to initialise the drive */
- if (vpo_detect(vpo)) {
+ if (!(dev = vpoio_probe(ppb, &vpo->vpo_io))) {
free(vpo, M_DEVBUF);
return (NULL);
}
- /* ok, go to next device on next probe */
- nvpo ++;
-
- return (&vpo->vpo_dev);
+ return (dev);
}
/*
@@ -183,6 +182,10 @@ vpoattach(struct ppb_device *dev)
struct scsibus_data *scbus;
struct vpo_data *vpo = vpodata[dev->id_unit];
+ /* low level attachment */
+ if (!vpoio_attach(&vpo->vpo_io))
+ return (0);
+
vpo->sc_link.adapter_unit = vpo->vpo_unit;
vpo->sc_link.adapter_targ = VP0_INITIATOR;
vpo->sc_link.adapter = &vpo_switch;
@@ -190,12 +193,6 @@ vpoattach(struct ppb_device *dev)
vpo->sc_link.opennings = VP0_OPENNINGS;
/*
- * Report ourselves
- */
- printf("vpo%d: <Adaptec aic7110 scsi> on ppbus %d\n",
- dev->id_unit, dev->ppb->ppb_link->adapter_unit);
-
- /*
* Prepare the scsibus_data area for the upperlevel
* scsi code.
*/
@@ -204,6 +201,9 @@ vpoattach(struct ppb_device *dev)
return (0);
scbus->adapter_link = &vpo->sc_link;
+ /* all went ok */
+ printf("vpo%d: <Iomega PPA-3/VPI0 SCSI controller>\n", dev->id_unit);
+
scsi_attachdevs(scbus);
return (1);
@@ -219,58 +219,11 @@ vpominphys(struct buf *bp)
return;
}
-#ifdef VP0_WARNING
-static __inline void
-vpo_warning(struct vpo_data *vpo, struct scsi_xfer *xs, int timeout)
-{
-
- switch (timeout) {
- case 0:
- case VP0_ESELECT_TIMEOUT:
- /* log(LOG_WARNING,
- "vpo%d: select timeout\n", vpo->vpo_unit); */
- break;
- case VP0_EDISCONNECT:
- log(LOG_WARNING,
- "vpo%d: can't get printer state\n", vpo->vpo_unit);
- break;
- case VP0_ECONNECT:
- log(LOG_WARNING,
- "vpo%d: can't get disk state\n", vpo->vpo_unit);
- break;
- case VP0_ECMD_TIMEOUT:
- log(LOG_WARNING,
- "vpo%d: command timeout\n", vpo->vpo_unit);
- break;
- case VP0_EPPDATA_TIMEOUT:
- log(LOG_WARNING,
- "vpo%d: EPP data timeout\n", vpo->vpo_unit);
- break;
- case VP0_ESTATUS_TIMEOUT:
- log(LOG_WARNING,
- "vpo%d: status timeout\n", vpo->vpo_unit);
- break;
- case VP0_EDATA_OVERFLOW:
- log(LOG_WARNING,
- "vpo%d: data overflow\n", vpo->vpo_unit);
- break;
- case VP0_EINTR:
- log(LOG_WARNING,
- "vpo%d: ppb request interrupted\n", vpo->vpo_unit);
- break;
- default:
- log(LOG_WARNING,
- "vpo%d: timeout = %d\n", vpo->vpo_unit, timeout);
- break;
- }
-}
-#endif /* VP0_WARNING */
-
/*
- * vpointr()
+ * vpo_intr()
*/
-static __inline void
-vpointr(struct vpo_data *vpo, struct scsi_xfer *xs)
+static void
+vpo_intr(struct vpo_data *vpo, struct scsi_xfer *xs)
{
int errno; /* error in errno.h */
@@ -278,9 +231,10 @@ vpointr(struct vpo_data *vpo, struct scsi_xfer *xs)
if (xs->datalen && !(xs->flags & SCSI_DATA_IN))
bcopy(xs->data, vpo->vpo_buffer, xs->datalen);
- errno = vpoio_do_scsi(vpo, VP0_INITIATOR,
+ errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
xs->sc_link->target, (char *)xs->cmd, xs->cmdlen,
- vpo->vpo_buffer, xs->datalen, &vpo->vpo_stat, &vpo->vpo_count);
+ vpo->vpo_buffer, xs->datalen, &vpo->vpo_stat, &vpo->vpo_count,
+ &vpo->vpo_error);
#ifdef VP0_DEBUG
printf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n",
@@ -298,9 +252,6 @@ vpointr(struct vpo_data *vpo, struct scsi_xfer *xs)
/* if a timeout occured, no sense */
if (vpo->vpo_error) {
-#ifdef VP0_WARNING
- vpo_warning(vpo, xs, vpo->vpo_error);
-#endif
xs->error = XS_TIMEOUT;
goto error;
}
@@ -318,11 +269,12 @@ vpointr(struct vpo_data *vpo, struct scsi_xfer *xs)
vpo->vpo_sense.cmd.length = sizeof(xs->sense);
vpo->vpo_sense.cmd.control = 0;
- errno = vpoio_do_scsi(vpo, VP0_INITIATOR,
+ errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
xs->sc_link->target, (char *)&vpo->vpo_sense.cmd,
sizeof(vpo->vpo_sense.cmd),
(char *)&xs->sense, sizeof(xs->sense),
- &vpo->vpo_sense.stat, &vpo->vpo_sense.count);
+ &vpo->vpo_sense.stat, &vpo->vpo_sense.count,
+ &vpo->vpo_error);
if (errno)
/* connection to ppbus interrupted */
@@ -376,490 +328,14 @@ vpo_scsi_cmd(struct scsi_xfer *xs)
#endif
if (xs->flags & SCSI_NOMASK) {
- vpointr(vpodata[xs->sc_link->adapter_unit], xs);
+ vpo_intr(vpodata[xs->sc_link->adapter_unit], xs);
return COMPLETE;
}
- s = VP0_SPL();
+ s = splbio();
- vpointr(vpodata[xs->sc_link->adapter_unit], xs);
+ vpo_intr(vpodata[xs->sc_link->adapter_unit], xs);
splx(s);
return SUCCESSFULLY_QUEUED;
}
-
-#define vpoio_d_pulse(vpo,b) { \
- ppb_wdtr(&(vpo)->vpo_dev, b); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
-}
-
-#define vpoio_c_pulse(vpo,b) { \
- ppb_wdtr(&(vpo)->vpo_dev, b); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
- ppb_wctr(&(vpo)->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
-}
-
-static int
-vpoio_disconnect(struct vpo_data *vpo)
-{
-
- vpoio_d_pulse(vpo, 0);
- vpoio_d_pulse(vpo, 0x3c);
- vpoio_d_pulse(vpo, 0x20);
- vpoio_d_pulse(vpo, 0xf);
-
- return (ppb_release_bus(&vpo->vpo_dev));
-}
-
-/*
- * how : PPB_WAIT or PPB_DONTWAIT
- */
-static int
-vpoio_connect(struct vpo_data *vpo, int how)
-{
- int error;
-
- if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
- return error;
-
- vpoio_c_pulse(vpo, 0);
- vpoio_c_pulse(vpo, 0x3c);
- vpoio_c_pulse(vpo, 0x20);
-
- if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) {
- vpoio_c_pulse(vpo, 0xcf);
- } else {
- vpoio_c_pulse(vpo, 0x8f);
- }
-
- return (0);
-}
-
-/*
- * vpoio_in_disk_mode()
- *
- * Check if we are in disk mode
- */
-static int
-vpoio_in_disk_mode(struct vpo_data *vpo)
-{
-
- /* first, set H_AUTO high */
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
-
- /* when H_AUTO is set low, H_FLT should be high */
- ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE);
- if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) == 0)
- return (0);
-
- /* when H_AUTO is set high, H_FLT should be low */
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
- if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) != 0)
- return (0);
-
- return (1);
-}
-
-/*
- * vpoio_reset()
- *
- * SCSI reset signal, the drive must be in disk mode
- */
-static void
-vpoio_reset (struct vpo_data *vpo)
-{
-
- /*
- * SCSI reset signal.
- */
- ppb_wdtr(&vpo->vpo_dev, (1 << 7));
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
- DELAY(25);
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
-
- return;
-}
-
-
-/*
- * vpo_detect()
- *
- * Detect and initialise the VP0 adapter.
- */
-static int
-vpo_detect(struct vpo_data *vpo)
-{
-
- vpoio_disconnect(vpo);
- vpoio_connect(vpo, PPB_DONTWAIT);
-
- if (!vpoio_in_disk_mode(vpo)) {
- vpoio_disconnect(vpo);
- return (VP0_EINITFAILED);
- }
-
- /* send SCSI reset signal */
- vpoio_reset (vpo);
-
- vpoio_disconnect(vpo);
-
- if (vpoio_in_disk_mode(vpo))
- return (VP0_EINITFAILED);
-
- return (0);
-}
-
-#define vpo_wctr(dev,byte,delay) { \
- int i; int iter = delay / MHZ_16_IO_DURATION; \
- for (i = 0; i < iter; i++) { \
- ppb_wctr(dev, byte); \
- } \
-}
-
-#define vpoio_spp_outbyte(vpo,byte) { \
- ppb_wdtr(&vpo->vpo_dev, byte); \
- ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
- vpo_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE, \
- VP0_SPP_WRITE_PULSE); \
-}
-
-#define vpoio_nibble_inbyte(vpo,buffer) { \
- register char h, l; \
- vpo_wctr(&vpo->vpo_dev, H_AUTO | H_SELIN | H_INIT | H_STROBE, \
- VP0_NIBBLE_READ_PULSE); \
- h = ppb_rstr(&vpo->vpo_dev); \
- ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE); \
- l = ppb_rstr(&vpo->vpo_dev); \
- *buffer = ((l >> 4) & 0x0f) + (h & 0xf0); \
-}
-
-#define vpoio_ps2_inbyte(vpo,buffer) { \
- *buffer = ppb_rdtr(&vpo->vpo_dev); \
- ppb_wctr(&vpo->vpo_dev, PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE); \
- ppb_wctr(&vpo->vpo_dev, PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE); \
-}
-
-/*
- * vpoio_outstr()
- */
-static int
-vpoio_outstr(struct vpo_data *vpo, char *buffer, int size)
-{
-
- register int k;
- int error = 0;
- int r, mode, epp;
-
- mode = ppb_get_mode(&vpo->vpo_dev);
- switch (mode) {
- case PPB_NIBBLE:
- case PPB_PS2:
- for (k = 0; k < size; k++) {
- vpoio_spp_outbyte(vpo, *buffer++);
- }
- break;
-
- case PPB_EPP:
- case PPB_ECP_EPP:
- epp = ppb_get_epp_protocol(&vpo->vpo_dev);
-
- ppb_reset_epp_timeout(&vpo->vpo_dev);
- ppb_wctr(&vpo->vpo_dev,
- H_AUTO | H_SELIN | H_INIT | H_STROBE);
-
- if (epp == EPP_1_7)
- for (k = 0; k < size; k++) {
- ppb_wepp(&vpo->vpo_dev, *buffer++);
- if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
- error = VP0_EPPDATA_TIMEOUT;
- break;
- }
- }
- else {
- if (((long) buffer | size) & 0x03)
- ppb_outsb_epp(&vpo->vpo_dev,
- buffer, size);
- else
- ppb_outsl_epp(&vpo->vpo_dev,
- buffer, size/4);
-
- if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
- error = VP0_EPPDATA_TIMEOUT;
- break;
- }
- }
- ppb_wctr(&vpo->vpo_dev,
- H_AUTO | H_nSELIN | H_INIT | H_STROBE);
- /* ppb_ecp_sync(&vpo->vpo_dev); */
- break;
-
- default:
- printf("vpoio_outstr(): unknown transfer mode (%d)!\n",
- mode);
- return (1); /* XXX */
- }
-
- return (error);
-}
-
-/*
- * vpoio_instr()
- */
-static int
-vpoio_instr(struct vpo_data *vpo, char *buffer, int size)
-{
-
- register int k;
- int error = 0;
- int r, mode, epp;
-
- mode = ppb_get_mode(&vpo->vpo_dev);
- switch (mode) {
- case PPB_NIBBLE:
- for (k = 0; k < size; k++) {
- vpoio_nibble_inbyte(vpo, buffer++);
- }
- ppb_wctr(&vpo->vpo_dev,
- H_AUTO | H_nSELIN | H_INIT | H_STROBE);
- break;
-
- case PPB_PS2:
- ppb_wctr(&vpo->vpo_dev, PCD |
- H_AUTO | H_SELIN | H_INIT | H_nSTROBE);
-
- for (k = 0; k < size; k++) {
- vpoio_ps2_inbyte(vpo, buffer++);
- }
- ppb_wctr(&vpo->vpo_dev,
- H_AUTO | H_nSELIN | H_INIT | H_STROBE);
- break;
-
- case PPB_EPP:
- case PPB_ECP_EPP:
- epp = ppb_get_epp_protocol(&vpo->vpo_dev);
-
- ppb_reset_epp_timeout(&vpo->vpo_dev);
- ppb_wctr(&vpo->vpo_dev, PCD |
- H_AUTO | H_SELIN | H_INIT | H_STROBE);
-
- if (epp == EPP_1_7)
- for (k = 0; k < size; k++) {
- *buffer++ = ppb_repp(&vpo->vpo_dev);
- if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
- error = VP0_EPPDATA_TIMEOUT;
- break;
- }
- }
- else {
- if (((long) buffer | size) & 0x03)
- ppb_insb_epp(&vpo->vpo_dev,
- buffer, size);
- else
- ppb_insl_epp(&vpo->vpo_dev,
- buffer, size/4);
-
- if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
- error = VP0_EPPDATA_TIMEOUT;
- break;
- }
- }
- ppb_wctr(&vpo->vpo_dev, PCD |
- H_AUTO | H_nSELIN | H_INIT | H_STROBE);
- /* ppb_ecp_sync(&vpo->vpo_dev); */
- break;
-
- default:
- printf("vpoio_instr(): unknown transfer mode (%d)!\n",
- mode);
- return (1); /* XXX */
- }
-
- return (error);
-}
-
-static __inline char
-vpoio_select(struct vpo_data *vpo, int initiator, int target)
-{
-
- register int k;
-
- ppb_wdtr(&vpo->vpo_dev, (1 << target));
- ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE);
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
- ppb_wdtr(&vpo->vpo_dev, (1 << initiator));
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
-
- k = 0;
- while (!(ppb_rstr(&vpo->vpo_dev) & 0x40) && (k++ < VP0_SELTMO))
- barrier();
-
- if (k >= VP0_SELTMO)
- return (VP0_ESELECT_TIMEOUT);
-
- return (0);
-}
-
-/*
- * vpoio_wait()
- *
- * H_SELIN must be low.
- */
-static __inline char
-vpoio_wait(struct vpo_data *vpo, int tmo)
-{
-
- register int k;
- register char r;
-
-#if 0 /* broken */
- if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
- return (0);
-
- return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
-#endif
-
- k = 0;
- while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
- barrier();
-
- /*
- * Return some status information.
- * Semantics : 0xc0 = ZIP wants more data
- * 0xd0 = ZIP wants to send more data
- * 0xe0 = ZIP wants command
- * 0xf0 = end of transfer, ZIP is sending status
- */
- if (k < tmo)
- return (r & 0xf0);
-
- return (0); /* command timed out */
-}
-
-static __inline int
-vpoio_do_scsi(struct vpo_data *vpo, int host, int target, char *command,
- int clen, char *buffer, int blen, int *result, int *count)
-{
-
- register char r;
- char l, h = 0;
- int rw, len, error = 0;
- register int k;
-
- /*
- * enter disk state, allocate the ppbus
- *
- * XXX
- * Should we allow this call to be interruptible?
- * The only way to report the interruption is to return
- * EIO do upper SCSI code :^(
- */
- if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
- return (error);
-
- if (!vpoio_in_disk_mode(vpo)) {
- vpo->vpo_error = VP0_ECONNECT; goto error;
- }
-
- if ((vpo->vpo_error = vpoio_select(vpo,host,target)))
- goto error;
-
- /*
- * Send the command ...
- *
- * set H_SELIN low for vpoio_wait().
- */
- ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
-
-#ifdef VP0_DEBUG
- printf("vpo%d: drive selected, now sending the command...\n",
- vpo->vpo_unit);
-#endif
-
- for (k = 0; k < clen; k++) {
- if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
- vpo->vpo_error = VP0_ECMD_TIMEOUT;
- goto error;
- }
- if (vpoio_outstr(vpo, &command[k], 1)) {
- vpo->vpo_error = VP0_EPPDATA_TIMEOUT;
- goto error;
- }
- }
-
-#ifdef VP0_DEBUG
- printf("vpo%d: command sent, now completing the request...\n",
- vpo->vpo_unit);
-#endif
-
- /*
- * Completion ...
- */
- rw = ((command[0] == READ_COMMAND) || (command[0] == READ_BIG) ||
- (command[0] == WRITE_COMMAND) || (command[0] == WRITE_BIG));
-
- *count = 0;
- for (;;) {
-
- if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
- vpo->vpo_error = VP0_ESTATUS_TIMEOUT; goto error;
- }
-
- /* stop when the ZIP wants to send status */
- if (r == (char)0xf0)
- break;
-
- if (*count >= blen) {
- vpo->vpo_error = VP0_EDATA_OVERFLOW;
- goto error;
- }
- len = (rw && ((blen - *count) >= VP0_SECTOR_SIZE)) ?
- VP0_SECTOR_SIZE : 1;
-
- /* ZIP wants to send data? */
- if (r == (char)0xc0)
- error = vpoio_outstr(vpo, &buffer[*count], len);
- else
- error = vpoio_instr(vpo, &buffer[*count], len);
-
- if (error) {
- vpo->vpo_error = error;
- goto error;
- }
-
- *count += len;
- }
-
- if (vpoio_instr(vpo, &l, 1)) {
- vpo->vpo_error = VP0_EOTHER; goto error;
- }
-
- /* check if the ZIP wants to send more status */
- if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
- if (vpoio_instr(vpo, &h, 1)) {
- vpo->vpo_error = VP0_EOTHER+2; goto error;
- }
-
- *result = ((int) h << 8) | ((int) l & 0xff);
-
-error:
- /* return to printer state, release the ppbus */
- vpoio_disconnect(vpo);
- return (0);
-}
diff --git a/sys/dev/ppbus/vpoio.c b/sys/dev/ppbus/vpoio.c
new file mode 100644
index 0000000..c853846
--- /dev/null
+++ b/sys/dev/ppbus/vpoio.c
@@ -0,0 +1,771 @@
+/*-
+ * Copyright (c) 1998 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: vpoio.c,v 1.1.2.4 1998/06/16 23:35:52 son Exp $
+ *
+ */
+
+#ifdef KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+
+#include <machine/clock.h>
+
+#endif /* KERNEL */
+
+#ifdef KERNEL
+#include <sys/kernel.h>
+#endif /*KERNEL */
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+#include <dev/ppbus/vpoio.h>
+
+/*
+ * The driver pools the drive. We may add a timeout queue to avoid
+ * active polling on nACK. I've tried this but it leads to unreliable
+ * transfers
+ */
+#define VP0_SELTMO 5000 /* select timeout */
+#define VP0_FAST_SPINTMO 500000 /* wait status timeout */
+#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */
+
+/*
+ * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
+ * but succeeding in respecting such timings leads to architecture
+ * dependent considerations.
+ */
+#define VP0_PULSE 1
+
+#define VP0_SECTOR_SIZE 512
+#define VP0_BUFFER_SIZE 0x12000
+
+#define n(flags) (~(flags) & (flags))
+
+/*
+ * VP0 connections.
+ */
+#define H_AUTO n(AUTOFEED)
+#define H_nAUTO AUTOFEED
+#define H_STROBE n(STROBE)
+#define H_nSTROBE STROBE
+#define H_BSY n(nBUSY)
+#define H_nBSY nBUSY
+#define H_SEL SELECT
+#define H_nSEL n(SELECT)
+#define H_ERR ERROR
+#define H_nERR n(ERROR)
+#define H_ACK nACK
+#define H_nACK n(nACK)
+#define H_FLT nFAULT
+#define H_nFLT n(nFAULT)
+#define H_SELIN n(SELECTIN)
+#define H_nSELIN SELECTIN
+#define H_INIT nINIT
+#define H_nINIT n(nINIT)
+
+/*
+ * Microcode to execute very fast I/O sequences at the lowest bus level.
+ */
+
+#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,(int)d_pulse)
+char d_pulse[] = {
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
+ H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
+ H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
+};
+
+#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,(int)c_pulse)
+char c_pulse[] = {
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
+ H_AUTO | H_SELIN | H_INIT | H_STROBE, 0,
+ H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
+ H_AUTO | H_SELIN | H_INIT | H_STROBE, 0,
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
+};
+
+struct ppb_microseq disconnect_microseq[] = {
+ MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
+ MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
+};
+
+struct ppb_microseq connect_epp_microseq[] = {
+ MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
+ MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
+};
+
+struct ppb_microseq connect_spp_microseq[] = {
+ MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
+ MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
+};
+
+/*
+ * nibble_inbyte_hook()
+ *
+ * Formats high and low nibble into a character
+ */
+static int
+nibble_inbyte_hook (void *p, char *ptr)
+{
+ struct vpo_nibble *s = (struct vpo_nibble *)p;
+
+ /* increment the buffer pointer */
+ *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
+
+ return (0);
+}
+
+/*
+ * Macro used to initialize each vpoio_data structure during
+ * low level attachment
+ */
+#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \
+ (vpo)->vpo_nibble_inbyte_msq[2].arg[2].p = \
+ (void *)&(vpo)->vpo_nibble.h; \
+ (vpo)->vpo_nibble_inbyte_msq[4].arg[2].p = \
+ (void *)&(vpo)->vpo_nibble.l; \
+ (vpo)->vpo_nibble_inbyte_msq[5].arg[0].f = \
+ nibble_inbyte_hook; \
+ (vpo)->vpo_nibble_inbyte_msq[5].arg[1].p = \
+ (void *)&(vpo)->vpo_nibble; \
+}
+
+/*
+ * This is the sub-microseqence for MS_GET in NIBBLE mode
+ * Retrieve the two nibbles and call the C function to generate the character
+ * and store it in the buffer (see nibble_inbyte_hook())
+ */
+struct ppb_microseq nibble_inbyte_submicroseq[] = {
+
+/* loop: */
+ MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),
+ MS_DELAY(VP0_PULSE),
+ MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
+ MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),
+ MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
+
+ /* do a C call to format the received nibbles */
+ MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
+ MS_DBRA(-6 /* loop */),
+
+ MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(0)
+};
+
+/*
+ * This is the sub-microseqence for MS_GET in PS2 mode
+ */
+struct ppb_microseq ps2_inbyte_submicroseq[] = {
+ MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
+
+/* loop: */
+ MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
+ MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
+ MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
+ MS_DBRA(-3 /* loop */),
+
+ MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(0)
+};
+
+/*
+ * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
+ */
+struct ppb_microseq spp_outbyte_submicroseq[] = {
+
+/* loop: */
+ MS_RASSERT_P(1, MS_REG_DTR),
+ MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_DELAY(VP0_PULSE),
+ MS_DBRA(-4 /* loop */),
+
+ /* return from the put call */
+ MS_RET(0)
+};
+
+/* EPP 1.7 microsequences, ptr and len set at runtime */
+struct ppb_microseq epp17_outstr_body[] = {
+ MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
+
+/* loop: */
+ MS_RASSERT_P(1, MS_REG_EPP),
+ MS_BRSET(TIMEOUT, 4 /* error */), /* EPP timeout? */
+ MS_DBRA(-2 /* loop */),
+
+ MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(0),
+/* error: */
+ MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(1)
+};
+
+struct ppb_microseq epp17_instr_body[] = {
+ MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
+
+/* loop: */
+ MS_RFETCH_P(1, MS_REG_EPP, MS_FETCH_ALL),
+ MS_BRSET(TIMEOUT, 4 /* error */), /* EPP timeout? */
+ MS_DBRA(-2 /* loop */),
+
+ MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(0),
+/* error: */
+ MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(1)
+};
+
+static int
+vpoio_disconnect(struct vpoio_data *vpo)
+{
+ int ret;
+
+ ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
+ return (ppb_release_bus(&vpo->vpo_dev));
+}
+
+/*
+ * how : PPB_WAIT or PPB_DONTWAIT
+ */
+static int
+vpoio_connect(struct vpoio_data *vpo, int how)
+{
+ int error;
+ int ret;
+
+ if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
+ return error;
+
+ if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
+ ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret);
+ else
+ ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
+
+ return (0);
+}
+
+/*
+ * vpoio_in_disk_mode()
+ *
+ * Check if we are in disk mode
+ *
+ * XXX should be ported to microseq with MS_ASSERT()
+ */
+static int
+vpoio_in_disk_mode(struct vpoio_data *vpo)
+{
+
+ /* first, set H_AUTO high */
+ ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
+
+ /* when H_AUTO is set low, H_FLT should be high */
+ ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE);
+ if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) == 0)
+ return (0);
+
+ /* when H_AUTO is set high, H_FLT should be low */
+ ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
+ if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) != 0)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * vpoio_reset()
+ *
+ * SCSI reset signal, the drive must be in disk mode
+ *
+ * XXX should be ported to microseq with MS_TRIG()
+ */
+static void
+vpoio_reset (struct vpoio_data *vpo)
+{
+
+ /*
+ * SCSI reset signal.
+ */
+ ppb_wdtr(&vpo->vpo_dev, (1 << VP0_INITIATOR));
+ ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
+ DELAY(25);
+ ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
+
+ return;
+}
+
+/*
+ * vpoio_detect()
+ *
+ * Detect and initialise the VP0 adapter.
+ */
+int
+vpoio_detect(struct vpoio_data *vpo)
+{
+
+ vpoio_disconnect(vpo);
+ vpoio_connect(vpo, PPB_DONTWAIT);
+
+ if (!vpoio_in_disk_mode(vpo)) {
+ vpoio_disconnect(vpo);
+ return (VP0_EINITFAILED);
+ }
+
+ /* send SCSI reset signal */
+ vpoio_reset(vpo);
+
+ vpoio_disconnect(vpo);
+
+ if (vpoio_in_disk_mode(vpo))
+ return (VP0_EINITFAILED);
+
+ return (0);
+}
+
+/*
+ * vpoio_outstr()
+ */
+static int
+vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
+{
+
+ int error = 0;
+
+ ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error);
+
+#if 0
+ /* XXX EPP 1.9 not implemented with microsequences */
+ else {
+
+ ppb_reset_epp_timeout(&vpo->vpo_dev);
+ ppb_wctr(&vpo->vpo_dev,
+ H_AUTO | H_SELIN | H_INIT | H_STROBE);
+
+ if (((long) buffer | size) & 0x03)
+ ppb_outsb_epp(&vpo->vpo_dev,
+ buffer, size);
+ else
+ ppb_outsl_epp(&vpo->vpo_dev,
+ buffer, size/4);
+
+ if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
+ error = VP0_EPPDATA_TIMEOUT;
+ goto error;
+ }
+
+ ppb_wctr(&vpo->vpo_dev,
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE);
+ }
+ /* ppb_ecp_sync(&vpo->vpo_dev); */
+#endif
+
+ return (error);
+}
+
+/*
+ * vpoio_instr()
+ */
+static int
+vpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
+{
+
+ register int k;
+ int error = 0;
+ int r, mode, epp;
+
+ ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error);
+
+#if 0
+ /* XXX EPP 1.9 not implemented with microsequences */
+ else {
+
+ ppb_reset_epp_timeout(&vpo->vpo_dev);
+ ppb_wctr(&vpo->vpo_dev, PCD |
+ H_AUTO | H_SELIN | H_INIT | H_STROBE);
+
+ if (((long) buffer | size) & 0x03)
+ ppb_insb_epp(&vpo->vpo_dev,
+ buffer, size);
+ else
+ ppb_insl_epp(&vpo->vpo_dev,
+ buffer, size/4);
+
+ if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
+ error = VP0_EPPDATA_TIMEOUT;
+ goto error;
+ }
+
+ ppb_wctr(&vpo->vpo_dev, PCD |
+ H_AUTO | H_nSELIN | H_INIT | H_STROBE);
+ }
+ /* ppb_ecp_sync(&vpo->vpo_dev); */
+#endif
+
+ return (error);
+}
+
+static char
+vpoio_select(struct vpoio_data *vpo, int initiator, int target)
+{
+ register int k;
+ int ret;
+
+ struct ppb_microseq select_microseq[] = {
+
+ /* parameter list
+ */
+ #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT)
+ #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT)
+
+ /* send the select command to the drive */
+ MS_DASS(MS_UNKNOWN),
+ MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_DASS(MS_UNKNOWN),
+ MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
+
+ /* now, wait until the drive is ready */
+ MS_SET(VP0_SELTMO),
+/* loop: */ MS_BRSET(H_ACK, 3 /* ready */),
+ MS_DBRA(-1 /* loop */),
+/* error: */ MS_RET(1),
+/* ready: */ MS_RET(0)
+ };
+
+ /* initialize the select microsequence */
+ ppb_MS_init_msq(select_microseq, 2,
+ SELECT_TARGET, 1 << target,
+ SELECT_INITIATOR, 1 << initiator);
+
+ ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret);
+
+ if (ret)
+ return (VP0_ESELECT_TIMEOUT);
+
+ return (0);
+}
+
+/*
+ * vpoio_wait()
+ *
+ * H_SELIN must be low.
+ *
+ * XXX should be ported to microseq
+ */
+static char
+vpoio_wait(struct vpoio_data *vpo, int tmo)
+{
+
+ register int k;
+ register char r;
+
+#if 0 /* broken */
+ if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
+ return (0);
+
+ return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
+#endif
+
+ /* XXX should be ported to microseq */
+ k = 0;
+ while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
+ ;
+
+ /*
+ * Return some status information.
+ * Semantics : 0xc0 = ZIP wants more data
+ * 0xd0 = ZIP wants to send more data
+ * 0xe0 = ZIP wants command
+ * 0xf0 = end of transfer, ZIP is sending status
+ */
+ if (k < tmo)
+ return (r & 0xf0);
+
+ return (0); /* command timed out */
+}
+
+/*
+ * vpoio_probe()
+ *
+ * Low level probe of vpo device
+ *
+ */
+struct ppb_device *
+vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo)
+{
+
+ /* ppbus dependent initialisation */
+ vpo->vpo_dev.id_unit = vpo->vpo_unit;
+ vpo->vpo_dev.name = "vpo";
+ vpo->vpo_dev.ppb = ppb;
+
+ /* now, try to initialise the drive */
+ if (vpoio_detect(vpo)) {
+ return (NULL);
+ }
+
+ return (&vpo->vpo_dev);
+}
+
+/*
+ * vpoio_attach()
+ *
+ * Low level attachment of vpo device
+ *
+ */
+int
+vpoio_attach(struct vpoio_data *vpo)
+{
+ int epp;
+
+ /*
+ * Report ourselves
+ */
+ printf("vpo%d: <Iomega VPI0 Parallel to SCSI adapter> on ppbus %d\n",
+ vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit);
+
+ /*
+ * Initialize microsequence code
+ */
+ vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
+ sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
+
+ if (!vpo->vpo_nibble_inbyte_msq)
+ return (0);
+
+ bcopy((void *)nibble_inbyte_submicroseq,
+ (void *)vpo->vpo_nibble_inbyte_msq,
+ sizeof(nibble_inbyte_submicroseq));
+
+ INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
+
+ /*
+ * Initialize mode dependent in/out microsequences
+ */
+ ppb_request_bus(&vpo->vpo_dev, PPB_WAIT);
+
+ /* enter NIBBLE mode to configure submsq */
+ if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) {
+
+ ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
+
+ ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
+ }
+
+ /* enter PS2 mode to configure submsq */
+ if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) {
+
+ ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq);
+
+ ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
+ }
+
+ epp = ppb_get_epp_protocol(&vpo->vpo_dev);
+
+ /* enter EPP mode to configure submsq */
+ if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
+
+ switch (epp) {
+ case EPP_1_9:
+ /* XXX EPP 1.9 support should be improved */
+ case EPP_1_7:
+ ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body);
+
+ ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body);
+ break;
+ default:
+ panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
+ epp);
+ }
+ }
+
+ /* try to enter EPP or PS/2 mode, NIBBLE otherwise */
+ if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
+ switch (epp) {
+ case EPP_1_9:
+ printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit);
+ break;
+ case EPP_1_7:
+ printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit);
+ break;
+ default:
+ panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
+ epp);
+ }
+ } else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1)
+ printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
+
+ else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1)
+ printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
+
+ else {
+ printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n",
+ vpo->vpo_unit);
+
+ ppb_release_bus(&vpo->vpo_dev);
+
+ free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
+ return (0);
+ }
+
+ ppb_release_bus(&vpo->vpo_dev);
+
+ return (1);
+}
+
+/*
+ * vpoio_reset_bus()
+ *
+ */
+int
+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) == 0)) {
+
+ /* release ppbus */
+ vpoio_disconnect(vpo);
+ return (1);
+ }
+
+ /* reset the SCSI bus */
+ vpoio_reset(vpo);
+
+ /* then disconnect */
+ vpoio_disconnect(vpo);
+
+ return (0);
+}
+
+/*
+ * vpoio_do_scsi()
+ *
+ * Send an SCSI command
+ *
+ */
+int
+vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
+ int clen, char *buffer, int blen, int *result, int *count,
+ int *ret)
+{
+
+ register char r;
+ char l, h = 0;
+ int len, error = 0;
+ register int k;
+
+ /*
+ * enter disk state, allocate the ppbus
+ *
+ * XXX
+ * Should we allow this call to be interruptible?
+ * The only way to report the interruption is to return
+ * EIO do upper SCSI code :^(
+ */
+ if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
+ return (error);
+
+ if (!vpoio_in_disk_mode(vpo)) {
+ *ret = VP0_ECONNECT; goto error;
+ }
+
+ if ((*ret = vpoio_select(vpo,host,target)))
+ goto error;
+
+ /*
+ * Send the command ...
+ *
+ * set H_SELIN low for vpoio_wait().
+ */
+ ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
+
+ for (k = 0; k < clen; k++) {
+ if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
+ *ret = VP0_ECMD_TIMEOUT;
+ goto error;
+ }
+ if (vpoio_outstr(vpo, &command[k], 1)) {
+ *ret = VP0_EPPDATA_TIMEOUT;
+ goto error;
+ }
+ }
+
+ /*
+ * Completion ...
+ */
+
+ *count = 0;
+ for (;;) {
+
+ if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
+ *ret = VP0_ESTATUS_TIMEOUT; goto error;
+ }
+
+ /* stop when the ZIP wants to send status */
+ if (r == (char)0xf0)
+ break;
+
+ if (*count >= blen) {
+ *ret = VP0_EDATA_OVERFLOW;
+ goto error;
+ }
+ len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
+ VP0_SECTOR_SIZE : 1;
+
+ /* ZIP wants to send data? */
+ if (r == (char)0xc0)
+ error = vpoio_outstr(vpo, &buffer[*count], len);
+ else
+ error = vpoio_instr(vpo, &buffer[*count], len);
+
+ if (error) {
+ *ret = error;
+ goto error;
+ }
+
+ *count += len;
+ }
+
+ if (vpoio_instr(vpo, &l, 1)) {
+ *ret = VP0_EOTHER; goto error;
+ }
+
+ /* check if the ZIP wants to send more status */
+ if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
+ if (vpoio_instr(vpo, &h, 1)) {
+ *ret = VP0_EOTHER+2; goto error;
+ }
+
+ *result = ((int) h << 8) | ((int) l & 0xff);
+
+error:
+ /* return to printer state, release the ppbus */
+ vpoio_disconnect(vpo);
+ return (0);
+}
diff --git a/sys/dev/ppbus/vpo.h b/sys/dev/ppbus/vpoio.h
index 5a724dd..40c5c01 100644
--- a/sys/dev/ppbus/vpo.h
+++ b/sys/dev/ppbus/vpoio.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1997 Nicolas Souchu
+ * Copyright (c) 1998 Nicolas Souchu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -23,21 +23,17 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: vpo.h,v 1.1 1997/08/14 13:57:45 msmith Exp $
+ * $Id: vpoio.h,v 1.1.2.3 1998/06/14 15:37:21 son Exp $
*
*/
-#ifndef __VP03_H
-#define __VP03_H
-
-#define barrier() __asm__("": : :"memory")
+#ifndef __VP0IO_H
+#define __VP0IO_H
+/*
+ * The ZIP drive cannot act as an initiator.
+ */
#define VP0_INITIATOR 0x7
-#define VP0_SECTOR_SIZE 512
-#define VP0_BUFFER_SIZE 0x12000
-
-#define VP0_SPL() splbio()
-
#define VP0_ESELECT_TIMEOUT 1
#define VP0_ECMD_TIMEOUT 2
#define VP0_ECONNECT 3
@@ -53,58 +49,36 @@
#define VP0_OPENNINGS 1
-#define n(flags) (~(flags) & (flags))
-
/*
- * VP0 timings.
+ * Data structure used during microsequence execution
+ * when characters are received in nibble mode
*/
-#define MHZ_16_IO_DURATION 62
+struct vpo_nibble {
+ char h; /* most significant nibble */
+ char l; /* less significant nibble */
+};
-#define VP0_SPP_WRITE_PULSE 253
-#define VP0_NIBBLE_READ_PULSE 486
+struct vpoio_data {
+ unsigned short int vpo_unit;
-/*
- * VP0 connections.
- */
-#define H_AUTO n(AUTOFEED)
-#define H_nAUTO AUTOFEED
-#define H_STROBE n(STROBE)
-#define H_nSTROBE STROBE
-#define H_BSY n(nBUSY)
-#define H_nBSY n_BUSY
-#define H_SEL SELECT
-#define H_nSEL n(SELECT)
-#define H_ERR ERROR
-#define H_nERR n(ERROR)
-#define H_ACK nACK
-#define H_nACK n(nACK)
-#define H_FLT nFAULT
-#define H_nFLT n(nFAULT)
-#define H_SELIN n(SELECTIN)
-#define H_nSELIN SELECTIN
-#define H_INIT nINIT
-#define H_nINIT n(nINIT)
+ struct vpo_nibble vpo_nibble;
-struct vpo_sense {
- struct scsi_sense cmd;
- unsigned int stat;
- unsigned int count;
-};
+ /* each device must have its own nibble inbyte microsequence */
+ struct ppb_microseq *vpo_nibble_inbyte_msq;
-struct vpo_data {
- unsigned short vpo_unit;
+ struct ppb_device vpo_dev;
+};
- int vpo_stat;
- int vpo_count;
- int vpo_error;
+#define vpoio_set_unit(vpo,unit) ((vpo)->vpo_unit = unit)
- struct ppb_status vpo_status;
- struct vpo_sense vpo_sense;
+struct ppb_device *vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo);
- unsigned char vpo_buffer[VP0_BUFFER_SIZE];
+int vpoio_attach(struct vpoio_data *vpo);
+int vpoio_detect(struct vpoio_data *vpo);
+int vpoio_reset_bus(struct vpoio_data *vpo);
- struct ppb_device vpo_dev;
- struct scsi_link sc_link;
-};
+int vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
+ int clen, char *buffer, int blen, int *result, int *count,
+ int *ret);
#endif
OpenPOWER on IntegriCloud