summaryrefslogtreecommitdiffstats
path: root/sys/dev/ppbus
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ppbus')
-rw-r--r--sys/dev/ppbus/if_plip.c784
-rw-r--r--sys/dev/ppbus/immio.c804
-rw-r--r--sys/dev/ppbus/lpbb.c224
-rw-r--r--sys/dev/ppbus/lpt.c974
-rw-r--r--sys/dev/ppbus/lpt.h66
-rw-r--r--sys/dev/ppbus/lptio.h24
-rw-r--r--sys/dev/ppbus/pcfclock.c342
-rw-r--r--sys/dev/ppbus/ppb_1284.c857
-rw-r--r--sys/dev/ppbus/ppb_1284.h126
-rw-r--r--sys/dev/ppbus/ppb_base.c195
-rw-r--r--sys/dev/ppbus/ppb_msq.c337
-rw-r--r--sys/dev/ppbus/ppb_msq.h205
-rw-r--r--sys/dev/ppbus/ppbconf.c558
-rw-r--r--sys/dev/ppbus/ppbconf.h276
-rw-r--r--sys/dev/ppbus/ppbio.h83
-rw-r--r--sys/dev/ppbus/ppbus_if.m93
-rw-r--r--sys/dev/ppbus/ppi.c578
-rw-r--r--sys/dev/ppbus/ppi.h54
-rw-r--r--sys/dev/ppbus/pps.c320
-rw-r--r--sys/dev/ppbus/vpo.c463
-rw-r--r--sys/dev/ppbus/vpoio.c780
-rw-r--r--sys/dev/ppbus/vpoio.h100
22 files changed, 8243 insertions, 0 deletions
diff --git a/sys/dev/ppbus/if_plip.c b/sys/dev/ppbus/if_plip.c
new file mode 100644
index 0000000..6f72096
--- /dev/null
+++ b/sys/dev/ppbus/if_plip.c
@@ -0,0 +1,784 @@
+/*-
+ * 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
+ * $FreeBSD$
+ */
+
+/*
+ * 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
+ */
+
+#include "opt_plip.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+#include <net/bpf.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include "ppbus_if.h"
+#include <dev/ppbus/ppbio.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 lprintf if (lptflag) printf
+
+#ifdef PLIP_DEBUG
+static int volatile lptflag = 1;
+#else
+static int volatile lptflag = 0;
+#endif
+
+struct lp_data {
+ unsigned short lp_unit;
+
+ struct ifnet sc_if;
+ u_char *sc_ifbuf;
+ int sc_iferrs;
+
+ struct resource *res_irq;
+};
+
+/* 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 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 lp_intr(void *);
+
+#define DEVTOSOFTC(dev) \
+ ((struct lp_data *)device_get_softc(dev))
+#define UNITOSOFTC(unit) \
+ ((struct lp_data *)devclass_get_softc(lp_devclass, (unit)))
+#define UNITODEVICE(unit) \
+ (devclass_get_device(lp_devclass, (unit)))
+
+static devclass_t lp_devclass;
+
+static void
+lp_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, "plip", -1);
+}
+/*
+ * lpprobe()
+ */
+static int
+lp_probe(device_t dev)
+{
+ device_t ppbus = device_get_parent(dev);
+ struct lp_data *lp;
+ int zero = 0;
+ uintptr_t irq;
+
+ lp = DEVTOSOFTC(dev);
+ bzero(lp, sizeof(struct lp_data));
+
+ /* retrieve the ppbus irq */
+ BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
+
+ /* if we haven't interrupts, the probe fails */
+ if (irq == -1) {
+ device_printf(dev, "not an interrupt driven port, failed.\n");
+ return (ENXIO);
+ }
+
+ /* reserve the interrupt resource, expecting irq is available to continue */
+ lp->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
+ RF_SHAREABLE);
+ if (lp->res_irq == 0) {
+ device_printf(dev, "cannot reserve interrupt, failed.\n");
+ return (ENXIO);
+ }
+
+ /*
+ * lp dependent initialisation.
+ */
+ lp->lp_unit = device_get_unit(dev);
+
+ device_set_desc(dev, "PLIP network interface");
+
+ return (0);
+}
+
+static int
+lp_attach (device_t dev)
+{
+ struct lp_data *lp = DEVTOSOFTC(dev);
+ struct ifnet *ifp = &lp->sc_if;
+
+ ifp->if_softc = lp;
+ ifp->if_name = "lp";
+ ifp->if_unit = device_get_unit(dev);
+ 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);
+
+ bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
+
+ return (0);
+}
+/*
+ * 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)
+{
+ device_t dev = UNITODEVICE(ifp->if_unit);
+ device_t ppbus = device_get_parent(dev);
+ struct lp_data *sc = DEVTOSOFTC(dev);
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ u_char *ptr;
+ void *ih;
+ 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(ppbus, 0x00);
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* IFF_UP is not set, try to release the bus anyway */
+ ppb_release_bus(ppbus, dev);
+ break;
+ }
+ if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
+
+ /* XXX
+ * Should the request be interruptible?
+ */
+ if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT|PPB_INTR)))
+ return (error);
+
+ /* Now IFF_UP means that we own the bus */
+
+ ppb_set_mode(ppbus, PPB_COMPATIBLE);
+
+ if (lpinittables()) {
+ ppb_release_bus(ppbus, dev);
+ return ENOBUFS;
+ }
+
+ sc->sc_ifbuf = malloc(sc->sc_if.if_mtu + MLPIPHDRLEN,
+ M_DEVBUF, M_WAITOK);
+ if (!sc->sc_ifbuf) {
+ ppb_release_bus(ppbus, dev);
+ return ENOBUFS;
+ }
+
+ /* attach our interrupt handler, later detached when the bus is released */
+ if ((error = BUS_SETUP_INTR(ppbus, dev, sc->res_irq,
+ INTR_TYPE_NET, lp_intr, dev, &ih))) {
+ ppb_release_bus(ppbus, dev);
+ return (error);
+ }
+
+ ppb_wctr(ppbus, IRQENABLE);
+ 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;
+
+ case SIOCGIFMEDIA:
+ /*
+ * No ifmedia support at this stage; maybe use it
+ * in future for eg. protocol selection.
+ */
+ return EINVAL;
+
+ default:
+ lprintf("LP:ioctl(0x%lx)\n", cmd);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static __inline int
+clpoutbyte (u_char byte, int spin, device_t ppbus)
+{
+ ppb_wdtr(ppbus, ctxmitl[byte]);
+ while (ppb_rstr(ppbus) & CLPIP_SHAKE)
+ if (--spin == 0) {
+ return 1;
+ }
+ ppb_wdtr(ppbus, ctxmith[byte]);
+ while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
+ if (--spin == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static __inline int
+clpinbyte (int spin, device_t ppbus)
+{
+ u_char c, cl;
+
+ while((ppb_rstr(ppbus) & CLPIP_SHAKE))
+ if(!--spin) {
+ return -1;
+ }
+ cl = ppb_rstr(ppbus);
+ ppb_wdtr(ppbus, 0x10);
+
+ while(!(ppb_rstr(ppbus) & CLPIP_SHAKE))
+ if(!--spin) {
+ return -1;
+ }
+ c = ppb_rstr(ppbus);
+ ppb_wdtr(ppbus, 0x00);
+
+ return (ctrecvl[cl] | ctrecvh[c]);
+}
+
+static void
+lptap(struct ifnet *ifp, struct mbuf *m)
+{
+ /*
+ * Send a packet through bpf. We need to prepend the address family
+ * as a four 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).
+ */
+ u_int32_t af = AF_INET;
+ struct mbuf m0;
+
+ m0.m_next = m;
+ m0.m_len = sizeof(u_int32_t);
+ m0.m_data = (char *)&af;
+ BPF_MTAP(ifp, &m0);
+}
+
+static void
+lp_intr (void *arg)
+{
+ device_t dev = (device_t)arg;
+ device_t ppbus = device_get_parent(dev);
+ struct lp_data *sc = DEVTOSOFTC(dev);
+ 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(ppbus, 0x01);
+
+ /* Get the packet length */
+ j = clpinbyte(LPMAXSPIN2, ppbus);
+ if (j == -1)
+ goto err;
+ len = j;
+ j = clpinbyte(LPMAXSPIN2, ppbus);
+ 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, ppbus);
+ if (j == -1) {
+ goto err;
+ }
+ *bp++ = j;
+ }
+ /* Get and ignore checksum */
+ j = clpinbyte(LPMAXSPIN2, ppbus);
+ if (j == -1) {
+ goto err;
+ }
+
+ len = bp - sc->sc_ifbuf;
+ if (len <= CLPIPHDRLEN)
+ goto err;
+
+ sc->sc_iferrs = 0;
+
+ 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 (sc->sc_if.if_bpf)
+ lptap(&sc->sc_if, top);
+ if (! IF_HANDOFF(&ipintrq, top, NULL)) {
+ lprintf("DROP");
+ } else {
+ schednetisr(NETISR_IP);
+ }
+ }
+ goto done;
+ }
+ while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
+ len = sc->sc_if.if_mtu + LPIPHDRLEN;
+ bp = sc->sc_ifbuf;
+ while (len--) {
+
+ cl = ppb_rstr(ppbus);
+ ppb_wdtr(ppbus, 8);
+
+ j = LPMAXSPIN2;
+ while((ppb_rstr(ppbus) & LPIP_SHAKE))
+ if(!--j) goto err;
+
+ c = ppb_rstr(ppbus);
+ ppb_wdtr(ppbus, 0);
+
+ *bp++= trecvh[cl] | trecvl[c];
+
+ j = LPMAXSPIN2;
+ while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) {
+ if (cl != c &&
+ (((cl = ppb_rstr(ppbus)) ^ 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;
+
+ 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 (sc->sc_if.if_bpf)
+ lptap(&sc->sc_if, top);
+ if (! IF_HANDOFF(&ipintrq, top, NULL)) {
+ lprintf("DROP");
+ } else {
+ schednetisr(NETISR_IP);
+ }
+ }
+ }
+ goto done;
+
+ err:
+ ppb_wdtr(ppbus, 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", device_get_unit(dev));
+ ppb_wctr(ppbus, 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, device_t ppbus)
+{
+ ppb_wdtr(ppbus, txmith[byte]);
+ while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
+ if (--spin == 0)
+ return 1;
+ ppb_wdtr(ppbus, txmitl[byte]);
+ while (ppb_rstr(ppbus) & LPIP_SHAKE)
+ if (--spin == 0)
+ return 1;
+ return 0;
+}
+
+static int
+lpoutput (struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt)
+{
+ device_t dev = UNITODEVICE(ifp->if_unit);
+ device_t ppbus = device_get_parent(dev);
+ int s, err;
+ struct mbuf *mm;
+ u_char *cp = "\0\0";
+ u_char chksum = 0;
+ int count = 0;
+ int i, len, 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(ppbus, IRQENABLE);
+
+ if (ifp->if_flags & IFF_LINK0) {
+
+ if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
+ lprintf("&");
+ lp_intr(dev);
+ }
+
+ /* Alert other end to pending packet */
+ spin = LPMAXSPIN1;
+ ppb_wdtr(ppbus, 0x08);
+ while ((ppb_rstr(ppbus) & 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, ppbus))
+ goto nend;
+ if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
+ goto nend;
+
+ /* Send dummy ethernet header */
+ for (i = 0; i < 12; i++) {
+ if (clpoutbyte(i, LPMAXSPIN1, ppbus))
+ goto nend;
+ chksum += i;
+ }
+
+ if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
+ goto nend;
+ if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
+ goto nend;
+ chksum += 0x08 + 0x00; /* Add into checksum */
+
+ mm = m;
+ do {
+ cp = mtod(mm, u_char *);
+ len = mm->m_len;
+ while (len--) {
+ chksum += *cp;
+ if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
+ goto nend;
+ }
+ } while ((mm = mm->m_next));
+
+ /* Send checksum */
+ if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
+ goto nend;
+
+ /* Go quiescent */
+ ppb_wdtr(ppbus, 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;
+ if (ifp->if_bpf)
+ lptap(ifp, m);
+ }
+
+ m_freem(m);
+
+ if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
+ lprintf("^");
+ lp_intr(dev);
+ }
+ (void) splx(s);
+ return 0;
+ }
+
+ if (ppb_rstr(ppbus) & LPIP_SHAKE) {
+ lprintf("&");
+ lp_intr(dev);
+ }
+
+ if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
+ goto end;
+ if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
+ goto end;
+
+ mm = m;
+ do {
+ cp = mtod(mm,u_char *);
+ len = mm->m_len;
+ while (len--)
+ if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
+ goto end;
+ } while ((mm = mm->m_next));
+
+ err = 0; /* no errors were encountered */
+
+ end:
+ --cp;
+ ppb_wdtr(ppbus, 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 (ifp->if_bpf)
+ lptap(ifp, m);
+ }
+
+ m_freem(m);
+
+ if (ppb_rstr(ppbus) & LPIP_SHAKE) {
+ lprintf("^");
+ lp_intr(dev);
+ }
+
+ (void) splx(s);
+ return 0;
+}
+
+static device_method_t lp_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, lp_identify),
+ DEVMETHOD(device_probe, lp_probe),
+ DEVMETHOD(device_attach, lp_attach),
+
+ { 0, 0 }
+};
+
+static driver_t lp_driver = {
+ "plip",
+ lp_methods,
+ sizeof(struct lp_data),
+};
+
+DRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0);
diff --git a/sys/dev/ppbus/immio.c b/sys/dev/ppbus/immio.c
new file mode 100644
index 0000000..dcdc34b
--- /dev/null
+++ b/sys/dev/ppbus/immio.c
@@ -0,0 +1,804 @@
+/*-
+ * Copyright (c) 1998, 1999 Nicolas Souchu
+ * Copyright (c) 2001 Alcove - 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * Iomega ZIP+ Matchmaker Parallel Port Interface driver
+ *
+ * Thanks to David Campbell work on the Linux driver and the Iomega specs
+ * Thanks to Thiebault Moeglin for the drive
+ */
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#endif /* _KERNEL */
+
+#include "opt_vpo.h"
+
+#include <dev/ppbus/ppbio.h>
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+#include <dev/ppbus/vpoio.h>
+#include <dev/ppbus/ppb_1284.h>
+
+#include "ppbus_if.h"
+
+#define VP0_SELTMO 5000 /* select timeout */
+#define VP0_FAST_SPINTMO 500000 /* wait status timeout */
+#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */
+
+#define VP0_SECTOR_SIZE 512
+
+/*
+ * Microcode to execute very fast I/O sequences at the lowest bus level.
+ */
+
+#define WAIT_RET MS_PARAM(7, 2, MS_TYP_PTR)
+#define WAIT_TMO MS_PARAM(1, 0, MS_TYP_INT)
+
+#define DECLARE_WAIT_MICROSEQUENCE \
+struct ppb_microseq wait_microseq[] = { \
+ MS_CASS(0x0c), \
+ MS_SET(MS_UNKNOWN), \
+ /* loop */ \
+ MS_BRSET(nBUSY, 4 /* ready */), \
+ MS_DBRA(-2 /* loop */), \
+ MS_CASS(0x04), \
+ MS_RET(1), /* timed out */ \
+ /* ready */ \
+ MS_CASS(0x04), \
+ MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN ), \
+ MS_RET(0) /* no error */ \
+}
+
+#define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA)
+
+#define DECLARE_SELECT_MICROSEQUENCE \
+struct ppb_microseq select_microseq[] = { \
+ MS_CASS(0xc), \
+ /* first, check there is nothing holding onto the bus */ \
+ MS_SET(VP0_SELTMO), \
+/* _loop: */ \
+ MS_BRCLEAR(0x8, 2 /* _ready */), \
+ MS_DBRA(-2 /* _loop */), \
+ MS_RET(2), /* bus busy */ \
+/* _ready: */ \
+ MS_CASS(0x4), \
+ MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \
+ MS_DELAY(1), \
+ MS_CASS(0xc), \
+ MS_CASS(0xd), \
+ /* now, wait until the drive is ready */ \
+ MS_SET(VP0_SELTMO), \
+/* loop: */ \
+ MS_BRSET(0x8, 3 /* ready */), \
+ MS_DBRA(-2 /* loop */), \
+/* error: */ \
+ MS_CASS(0xc), \
+ MS_RET(VP0_ESELECT_TIMEOUT), \
+/* ready: */ \
+ MS_CASS(0xc), \
+ MS_RET(0) \
+}
+
+static struct ppb_microseq transfer_epilog[] = {
+ MS_CASS(0x4),
+ MS_CASS(0xc),
+ MS_CASS(0xe),
+ MS_CASS(0x4),
+ MS_RET(0)
+};
+
+#define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR)
+#define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR)
+#define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR)
+#define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA)
+
+#define DECLARE_CPP_MICROSEQ \
+struct ppb_microseq cpp_microseq[] = { \
+ MS_CASS(0x0c), MS_DELAY(2), \
+ MS_DASS(0xaa), MS_DELAY(10), \
+ MS_DASS(0x55), MS_DELAY(10), \
+ MS_DASS(0x00), MS_DELAY(10), \
+ MS_DASS(0xff), MS_DELAY(10), \
+ MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \
+ MS_DASS(0x87), MS_DELAY(10), \
+ MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \
+ MS_DASS(0x78), MS_DELAY(10), \
+ MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \
+ MS_DASS(MS_UNKNOWN /* param */), \
+ MS_DELAY(2), \
+ MS_CASS(0x0c), MS_DELAY(10), \
+ MS_CASS(0x0d), MS_DELAY(2), \
+ MS_CASS(0x0c), MS_DELAY(10), \
+ MS_DASS(0xff), MS_DELAY(10), \
+ MS_RET(0) \
+}
+
+#define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA)
+
+#define DECLARE_NEGOCIATE_MICROSEQ \
+struct ppb_microseq negociate_microseq[] = { \
+ MS_CASS(0x4), \
+ MS_DELAY(5), \
+ MS_DASS(MS_UNKNOWN /* mode */), \
+ MS_DELAY(100), \
+ MS_CASS(0x6), \
+ MS_DELAY(5), \
+ MS_BRSET(0x20, 5 /* continue */), \
+ MS_DELAY(5), \
+ MS_CASS(0x7), \
+ MS_DELAY(5), \
+ MS_CASS(0x6), \
+ MS_RET(VP0_ENEGOCIATE), \
+/* continue: */ \
+ MS_DELAY(5), \
+ MS_CASS(0x7), \
+ MS_DELAY(5), \
+ MS_CASS(0x6), \
+ MS_RET(0) \
+}
+
+#define INB_NIBBLE_L MS_PARAM(3, 2, MS_TYP_PTR)
+#define INB_NIBBLE_H MS_PARAM(6, 2, MS_TYP_PTR)
+#define INB_NIBBLE_F MS_PARAM(9, 0, MS_TYP_FUN)
+#define INB_NIBBLE_P MS_PARAM(9, 1, MS_TYP_PTR)
+
+/*
+ * 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())
+ */
+
+#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \
+struct ppb_microseq nibble_inbyte_submicroseq[] = { \
+ MS_CASS(0x4), \
+/* loop: */ \
+ MS_CASS(0x6), \
+ MS_DELAY(1), \
+ MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\
+ MS_CASS(0x5), \
+ MS_DELAY(1), \
+ MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\
+ MS_CASS(0x4), \
+ MS_DELAY(1), \
+ /* do a C call to format the received nibbles */ \
+ MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), \
+ MS_DBRA(-7 /* loop */), \
+ MS_RET(0) \
+}
+
+static struct ppb_microseq reset_microseq[] = {
+ MS_CASS(0x04),
+ MS_DASS(0x40),
+ MS_DELAY(1),
+ MS_CASS(0x0c),
+ MS_CASS(0x0d),
+ MS_DELAY(50),
+ MS_CASS(0x0c),
+ MS_CASS(0x04),
+ 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);
+}
+
+/*
+ * This is the sub-microseqence for MS_GET in PS2 mode
+ */
+static struct ppb_microseq ps2_inbyte_submicroseq[] = {
+ MS_CASS(0x4),
+
+/* loop: */
+ MS_CASS(PCD | 0x6),
+ MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
+ MS_CASS(PCD | 0x5),
+ MS_DBRA(-4 /* loop */),
+
+ MS_RET(0)
+};
+
+/*
+ * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
+ */
+static struct ppb_microseq spp_outbyte_submicroseq[] = {
+ MS_CASS(0x4),
+
+/* loop: */
+ MS_RASSERT_P(1, MS_REG_DTR),
+ MS_CASS(0x5),
+ MS_DBRA(0), /* decrement counter */
+ MS_RASSERT_P(1, MS_REG_DTR),
+ MS_CASS(0x0),
+ MS_DBRA(-6 /* loop */),
+
+ /* return from the put call */
+ MS_CASS(0x4),
+ MS_RET(0)
+};
+
+/* EPP 1.7 microsequences, ptr and len set at runtime */
+static struct ppb_microseq epp17_outstr[] = {
+ MS_CASS(0x4),
+ MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D),
+ MS_CASS(0xc),
+ MS_RET(0),
+};
+
+static struct ppb_microseq epp17_instr[] = {
+ MS_CASS(PCD | 0x4),
+ MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL),
+ MS_CASS(PCD | 0xc),
+ MS_RET(0),
+};
+
+static int
+imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
+{
+ DECLARE_CPP_MICROSEQ;
+
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ char s1, s2, s3;
+ int ret;
+
+ /* all should be ok */
+ if (connected)
+ *connected = 0;
+
+ ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
+ CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
+ CPP_PARAM, 0x30);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
+
+ if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
+ if (bootverbose)
+ printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
+ vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
+ if (connected)
+ *connected = VP0_ECONNECT;
+ }
+
+ if (release_bus)
+ return (ppb_release_bus(ppbus, vpo->vpo_dev));
+ else
+ return (0);
+}
+
+/*
+ * how : PPB_WAIT or PPB_DONTWAIT
+ */
+static int
+imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
+{
+ DECLARE_CPP_MICROSEQ;
+
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ char s1, s2, s3;
+ int error;
+ int ret;
+
+ /* all should be ok */
+ if (disconnected)
+ *disconnected = 0;
+
+ if (request_bus)
+ if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how)))
+ return (error);
+
+ ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
+ CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
+
+ /* select device 0 in compatible mode */
+ ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
+
+ /* disconnect all devices */
+ ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
+
+ if (PPB_IN_EPP_MODE(ppbus))
+ ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
+ else
+ ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
+
+ if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
+ if (bootverbose)
+ printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n",
+ vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
+ if (disconnected)
+ *disconnected = VP0_ECONNECT;
+ }
+
+ return (0);
+}
+
+/*
+ * imm_detect()
+ *
+ * Detect and initialise the VP0 adapter.
+ */
+static int
+imm_detect(struct vpoio_data *vpo)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error;
+
+ if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
+ return (error);
+
+ /* disconnect the drive, keep the bus */
+ imm_disconnect(vpo, NULL, 0);
+
+ vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
+ error = 1;
+
+ /* try to enter EPP mode since vpoio failure put the bus in NIBBLE */
+ if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
+ imm_connect(vpo, PPB_DONTWAIT, &error, 0);
+ }
+
+ /* if connection failed try PS/2 then NIBBLE modes */
+ if (error) {
+ if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
+ imm_connect(vpo, PPB_DONTWAIT, &error, 0);
+ }
+ if (error) {
+ if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
+ imm_connect(vpo, PPB_DONTWAIT, &error, 0);
+ if (error)
+ goto error;
+ vpo->vpo_mode_found = VP0_MODE_NIBBLE;
+ } else {
+ printf("imm%d: NIBBLE mode unavailable!\n", vpo->vpo_unit);
+ goto error;
+ }
+ } else {
+ vpo->vpo_mode_found = VP0_MODE_PS2;
+ }
+ } else {
+ vpo->vpo_mode_found = VP0_MODE_EPP;
+ }
+
+ /* send SCSI reset signal */
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
+
+ /* release the bus now */
+ imm_disconnect(vpo, &error, 1);
+
+ /* ensure we are disconnected or daisy chained peripheral
+ * may cause serious problem to the disk */
+
+ if (error) {
+ if (bootverbose)
+ printf("imm%d: can't disconnect from the drive\n",
+ vpo->vpo_unit);
+ goto error;
+ }
+
+ return (0);
+
+error:
+ ppb_release_bus(ppbus, vpo->vpo_dev);
+ return (VP0_EINITFAILED);
+}
+
+/*
+ * imm_outstr()
+ */
+static int
+imm_outstr(struct vpoio_data *vpo, char *buffer, int size)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error = 0;
+
+ if (PPB_IN_EPP_MODE(ppbus))
+ ppb_reset_epp_timeout(ppbus);
+
+ ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
+ (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
+
+ return (error);
+}
+
+/*
+ * imm_instr()
+ */
+static int
+imm_instr(struct vpoio_data *vpo, char *buffer, int size)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error = 0;
+
+ if (PPB_IN_EPP_MODE(ppbus))
+ ppb_reset_epp_timeout(ppbus);
+
+ ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
+ (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
+
+ return (error);
+}
+
+static char
+imm_select(struct vpoio_data *vpo, int initiator, int target)
+{
+ DECLARE_SELECT_MICROSEQUENCE;
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int ret;
+
+ /* initialize the select microsequence */
+ ppb_MS_init_msq(select_microseq, 1,
+ SELECT_TARGET, 1 << initiator | 1 << target);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
+
+ return (ret);
+}
+
+/*
+ * imm_wait()
+ *
+ * H_SELIN must be low.
+ *
+ */
+static char
+imm_wait(struct vpoio_data *vpo, int tmo)
+{
+ DECLARE_WAIT_MICROSEQUENCE;
+
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int ret, err;
+
+ /*
+ * Return some status information.
+ * Semantics : 0x88 = ZIP+ wants more data
+ * 0x98 = ZIP+ wants to send more data
+ * 0xa8 = ZIP+ wants command
+ * 0xb8 = end of transfer, ZIP+ is sending status
+ */
+
+ ppb_MS_init_msq(wait_microseq, 2,
+ WAIT_RET, (void *)&ret,
+ WAIT_TMO, tmo);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
+
+ if (err)
+ return (0); /* command timed out */
+
+ return(ret);
+}
+
+static int
+imm_negociate(struct vpoio_data *vpo)
+{
+ DECLARE_NEGOCIATE_MICROSEQ;
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int negociate_mode;
+ int ret;
+
+ if (PPB_IN_NIBBLE_MODE(ppbus))
+ negociate_mode = 0;
+ else if (PPB_IN_PS2_MODE(ppbus))
+ negociate_mode = 1;
+ else
+ return (0);
+
+#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
+ ret = ppb_1284_negociate(ppbus, negociate_mode);
+
+ if (ret)
+ return (VP0_ENEGOCIATE);
+#endif
+
+ ppb_MS_init_msq(negociate_microseq, 1,
+ NEGOCIATED_MODE, negociate_mode);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret);
+
+ return (ret);
+}
+
+/*
+ * imm_probe()
+ *
+ * Low level probe of vpo device
+ *
+ */
+int
+imm_probe(device_t dev, struct vpoio_data *vpo)
+{
+ int error;
+
+ /* ppbus dependent initialisation */
+ vpo->vpo_dev = dev;
+
+ /* now, try to initialise the drive */
+ if ((error = imm_detect(vpo))) {
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * imm_attach()
+ *
+ * Low level attachment of vpo device
+ *
+ */
+int
+imm_attach(struct vpoio_data *vpo)
+{
+ DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error = 0;
+
+ /*
+ * 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 (ENXIO);
+
+ bcopy((void *)nibble_inbyte_submicroseq,
+ (void *)vpo->vpo_nibble_inbyte_msq,
+ sizeof(nibble_inbyte_submicroseq));
+
+ ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
+ INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
+ INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
+ INB_NIBBLE_F, nibble_inbyte_hook,
+ INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble);
+
+ /*
+ * Initialize mode dependent in/out microsequences
+ */
+ if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
+ goto error;
+
+ /* ppbus automatically restore the last mode entered during detection */
+ switch (vpo->vpo_mode_found) {
+ case VP0_MODE_EPP:
+ ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr);
+ ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr);
+ printf("imm%d: EPP mode\n", vpo->vpo_unit);
+ break;
+ case VP0_MODE_PS2:
+ ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
+ ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
+ printf("imm%d: PS2 mode\n", vpo->vpo_unit);
+ break;
+ case VP0_MODE_NIBBLE:
+ ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
+ ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
+ printf("imm%d: NIBBLE mode\n", vpo->vpo_unit);
+ break;
+ default:
+ panic("imm: unknown mode %d", vpo->vpo_mode_found);
+ }
+
+ ppb_release_bus(ppbus, vpo->vpo_dev);
+ error:
+ return (error);
+}
+
+/*
+ * imm_reset_bus()
+ *
+ */
+int
+imm_reset_bus(struct vpoio_data *vpo)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int disconnected;
+
+ /* first, connect to the drive and request the bus */
+ imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
+
+ if (!disconnected) {
+
+ /* reset the SCSI bus */
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
+
+ /* then disconnect */
+ imm_disconnect(vpo, NULL, 1);
+ }
+
+ return (0);
+}
+
+/*
+ * imm_do_scsi()
+ *
+ * Send an SCSI command
+ *
+ */
+int
+imm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
+ int clen, char *buffer, int blen, int *result, int *count,
+ int *ret)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ register char r;
+ char l, h = 0;
+ int len, error = 0, not_connected = 0;
+ register int k;
+ int negociated = 0;
+
+ /*
+ * 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 to upper SCSI code :^(
+ */
+ if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, &not_connected, 1)))
+ return (error);
+
+ if (not_connected) {
+ *ret = VP0_ECONNECT; goto error;
+ }
+
+ /*
+ * Select the drive ...
+ */
+ if ((*ret = imm_select(vpo,host,target)))
+ goto error;
+
+ /*
+ * Send the command ...
+ */
+ for (k = 0; k < clen; k+=2) {
+ if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
+ *ret = VP0_ECMD_TIMEOUT;
+ goto error;
+ }
+ if (imm_outstr(vpo, &command[k], 2)) {
+ *ret = VP0_EPPDATA_TIMEOUT;
+ goto error;
+ }
+ }
+
+ if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
+ *ret = VP0_ESTATUS_TIMEOUT; goto error;
+ }
+
+ if ((r & 0x30) == 0x10) {
+ if (imm_negociate(vpo)) {
+ *ret = VP0_ENEGOCIATE;
+ goto error;
+ } else
+ negociated = 1;
+ }
+
+ /*
+ * Complete transfer ...
+ */
+ *count = 0;
+ for (;;) {
+
+ if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
+ *ret = VP0_ESTATUS_TIMEOUT; goto error;
+ }
+
+ /* stop when the ZIP+ wants to send status */
+ if (r == (char)0xb8)
+ break;
+
+ if (*count >= blen) {
+ *ret = VP0_EDATA_OVERFLOW;
+ goto error;
+ }
+
+ /* ZIP+ wants to send data? */
+ if (r == (char)0x88) {
+ len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
+ VP0_SECTOR_SIZE : 2;
+
+ error = imm_outstr(vpo, &buffer[*count], len);
+ } else {
+ if (!PPB_IN_EPP_MODE(ppbus))
+ len = 1;
+ else
+ len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
+ VP0_SECTOR_SIZE : 1;
+
+ error = imm_instr(vpo, &buffer[*count], len);
+ }
+
+ if (error) {
+ *ret = error;
+ goto error;
+ }
+
+ *count += len;
+ }
+
+ if ((PPB_IN_NIBBLE_MODE(ppbus) ||
+ PPB_IN_PS2_MODE(ppbus)) && negociated)
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
+
+ /*
+ * Retrieve status ...
+ */
+ if (imm_negociate(vpo)) {
+ *ret = VP0_ENEGOCIATE;
+ goto error;
+ } else
+ negociated = 1;
+
+ if (imm_instr(vpo, &l, 1)) {
+ *ret = VP0_EOTHER; goto error;
+ }
+
+ /* check if the ZIP+ wants to send more status */
+ if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
+ if (imm_instr(vpo, &h, 1)) {
+ *ret = VP0_EOTHER+2; goto error;
+ }
+
+ /* Experience showed that we should discard this */
+ if (h == -1)
+ h = 0;
+
+ *result = ((int) h << 8) | ((int) l & 0xff);
+
+error:
+ if ((PPB_IN_NIBBLE_MODE(ppbus) ||
+ PPB_IN_PS2_MODE(ppbus)) && negociated)
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
+
+ /* return to printer state, release the ppbus */
+ imm_disconnect(vpo, NULL, 1);
+
+ return (0);
+}
diff --git a/sys/dev/ppbus/lpbb.c b/sys/dev/ppbus/lpbb.c
new file mode 100644
index 0000000..2b3dfff
--- /dev/null
+++ b/sys/dev/ppbus/lpbb.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 1998, 2001 Nicolas Souchu, Marc Bouget
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * I2C Bit-Banging over parallel port
+ *
+ * See the Official Philips interface description in lpbb(4)
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/uio.h>
+
+
+#include <dev/ppbus/ppbconf.h>
+#include "ppbus_if.h"
+#include <dev/ppbus/ppbio.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+static int lpbb_detect(device_t dev);
+
+static void
+lpbb_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, "lpbb", -1);
+}
+
+static int
+lpbb_probe(device_t dev)
+{
+
+ /* Perhaps call this during identify instead? */
+ if (!lpbb_detect(dev))
+ return (ENXIO);
+
+ device_set_desc(dev, "Parallel I2C bit-banging interface");
+
+ return (0);
+}
+
+static int
+lpbb_attach(device_t dev)
+{
+ device_t bitbang;
+
+ /* add generic bit-banging code */
+ bitbang = device_add_child(dev, "iicbb", -1);
+ device_probe_and_attach(bitbang);
+
+ return (0);
+}
+
+static int
+lpbb_callback(device_t dev, int index, caddr_t *data)
+{
+ device_t ppbus = device_get_parent(dev);
+ int error = 0;
+ int how;
+
+ switch (index) {
+ case IIC_REQUEST_BUS:
+ /* request the ppbus */
+ how = *(int *)data;
+ error = ppb_request_bus(ppbus, dev, how);
+ break;
+
+ case IIC_RELEASE_BUS:
+ /* release the ppbus */
+ error = ppb_release_bus(ppbus, dev);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+
+ return (error);
+}
+
+#define SDA_out 0x80
+#define SCL_out 0x08
+#define SDA_in 0x80
+#define SCL_in 0x08
+#define ALIM 0x20
+#define I2CKEY 0x50
+
+static int lpbb_getscl(device_t dev)
+{
+ return ((ppb_rstr(device_get_parent(dev)) & SCL_in) == SCL_in);
+}
+
+static int lpbb_getsda(device_t dev)
+{
+ return ((ppb_rstr(device_get_parent(dev)) & SDA_in) == SDA_in);
+}
+
+static void lpbb_setsda(device_t dev, char val)
+{
+ device_t ppbus = device_get_parent(dev);
+
+ if(val==0)
+ ppb_wdtr(ppbus, (u_char)SDA_out);
+ else
+ ppb_wdtr(ppbus, (u_char)~SDA_out);
+}
+
+static void lpbb_setscl(device_t dev, unsigned char val)
+{
+ device_t ppbus = device_get_parent(dev);
+
+ if(val==0)
+ ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)&~SCL_out));
+ else
+ ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)|SCL_out));
+}
+
+static int lpbb_detect(device_t dev)
+{
+ device_t ppbus = device_get_parent(dev);
+
+ if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
+ device_printf(dev, "can't allocate ppbus\n");
+ return (0);
+ }
+
+ /* reset bus */
+ lpbb_setsda(dev, 1);
+ lpbb_setscl(dev, 1);
+
+ if ((ppb_rstr(ppbus) & I2CKEY) ||
+ ((ppb_rstr(ppbus) & ALIM) != ALIM)) {
+
+ ppb_release_bus(ppbus, dev);
+ return (0);
+ }
+
+ ppb_release_bus(ppbus, dev);
+
+ return (1);
+}
+
+static int
+lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr)
+{
+ device_t ppbus = device_get_parent(dev);
+
+ if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
+ device_printf(dev, "can't allocate ppbus\n");
+ return (0);
+ }
+
+ /* reset bus */
+ lpbb_setsda(dev, 1);
+ lpbb_setscl(dev, 1);
+
+ ppb_release_bus(ppbus, dev);
+
+ return (IIC_ENOADDR);
+}
+
+static devclass_t lpbb_devclass;
+
+static device_method_t lpbb_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, lpbb_identify),
+ DEVMETHOD(device_probe, lpbb_probe),
+ DEVMETHOD(device_attach, lpbb_attach),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ /* iicbb interface */
+ DEVMETHOD(iicbb_callback, lpbb_callback),
+ DEVMETHOD(iicbb_setsda, lpbb_setsda),
+ DEVMETHOD(iicbb_setscl, lpbb_setscl),
+ DEVMETHOD(iicbb_getsda, lpbb_getsda),
+ DEVMETHOD(iicbb_getscl, lpbb_getscl),
+ DEVMETHOD(iicbb_reset, lpbb_reset),
+
+ { 0, 0 }
+};
+
+static driver_t lpbb_driver = {
+ "lpbb",
+ lpbb_methods,
+ 1,
+};
+
+DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0);
+MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
+MODULE_VERSION(lpbb, 1);
diff --git a/sys/dev/ppbus/lpt.c b/sys/dev/ppbus/lpt.c
new file mode 100644
index 0000000..9364c50
--- /dev/null
+++ b/sys/dev/ppbus/lpt.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (c) 1990 William F. Jolitz, TeleMuse
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This software is a component of "386BSD" developed by
+ * William F. Jolitz, TeleMuse.
+ * 4. Neither the name of the developer nor the name "386BSD"
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
+ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
+ * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
+ * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
+ * NOT MAKE USE OF THIS WORK.
+ *
+ * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
+ * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
+ * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
+ * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
+ * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
+ * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
+ * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
+ * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: unknown origin, 386BSD 0.1
+ * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
+ * From Id: nlpt.c,v 1.14 1999/02/08 13:55:43 des Exp
+ * $FreeBSD$
+ */
+
+/*
+ * Device Driver for AT parallel printer port
+ * Written by William Jolitz 12/18/90
+ */
+
+/*
+ * Updated for ppbus by Nicolas Souchu
+ * [Mon Jul 28 1997]
+ */
+
+#include "opt_lpt.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/ppbus/lptio.h>
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_1284.h>
+#include <dev/ppbus/lpt.h>
+#include "ppbus_if.h"
+#include <dev/ppbus/ppbio.h>
+
+#ifndef LPT_DEBUG
+#define lprintf(args)
+#else
+#define lprintf(args) \
+ do { \
+ if (lptflag) \
+ printf args; \
+ } while (0)
+static int volatile lptflag = 1;
+#endif
+
+#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
+#define LPTOUTINITIAL 10 /* initial timeout to wait for ready 1/10 s */
+#define LPTOUTMAX 1 /* maximal timeout 1 s */
+#define LPPRI (PZERO+8)
+#define BUFSIZE 1024
+#define BUFSTATSIZE 32
+
+#define LPTUNIT(s) ((s)&0x03)
+#define LPTFLAGS(s) ((s)&0xfc)
+
+struct lpt_data {
+
+ short sc_state;
+ /* default case: negative prime, negative ack, handshake strobe,
+ prime once */
+ u_char sc_control;
+ char sc_flags;
+#define LP_POS_INIT 0x04 /* if we are a postive init signal */
+#define LP_POS_ACK 0x08 /* if we are a positive going ack */
+#define LP_NO_PRIME 0x10 /* don't prime the printer at all */
+#define LP_PRIMEOPEN 0x20 /* prime on every open */
+#define LP_AUTOLF 0x40 /* tell printer to do an automatic lf */
+#define LP_BYPASS 0x80 /* bypass printer ready checks */
+ void *sc_inbuf;
+ void *sc_statbuf;
+ short sc_xfercnt ;
+ char sc_primed;
+ char *sc_cp ;
+ u_short sc_irq ; /* IRQ status of port */
+#define LP_HAS_IRQ 0x01 /* we have an irq available */
+#define LP_USE_IRQ 0x02 /* we are using our irq */
+#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */
+#define LP_ENABLE_EXT 0x10 /* we shall use advanced mode when possible */
+ u_char sc_backoff ; /* time to call lptout() again */
+
+ struct resource *intr_resource; /* interrupt resource */
+ void *intr_cookie; /* interrupt registration cookie */
+
+};
+
+#define LPT_NAME "lpt" /* our official name */
+
+static timeout_t lptout;
+static int lpt_port_test(device_t dev, u_char data, u_char mask);
+static int lpt_detect(device_t dev);
+
+#define DEVTOSOFTC(dev) \
+ ((struct lpt_data *)device_get_softc(dev))
+#define UNITOSOFTC(unit) \
+ ((struct lpt_data *)devclass_get_softc(lpt_devclass, (unit)))
+#define UNITODEVICE(unit) \
+ (devclass_get_device(lpt_devclass, (unit)))
+
+static void lptintr(device_t dev);
+static void lpt_intr(void *arg); /* without spls */
+
+static devclass_t lpt_devclass;
+
+
+/* bits for state */
+#define OPEN (1<<0) /* device is open */
+#define ASLP (1<<1) /* awaiting draining of printer */
+#define EERROR (1<<2) /* error was received from printer */
+#define OBUSY (1<<3) /* printer is busy doing output */
+#define LPTOUT (1<<4) /* timeout while not selected */
+#define TOUT (1<<5) /* timeout while not selected */
+#define 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 */
+
+
+/* status masks to interrogate printer status */
+#define RDY_MASK (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR) /* ready ? */
+#define LP_READY (LPS_SEL|LPS_NBSY|LPS_NERR)
+
+/* Printer Ready condition - from lpa.c */
+/* Only used in polling code */
+#define LPS_INVERT (LPS_NBSY | LPS_NACK | LPS_SEL | LPS_NERR)
+#define LPS_MASK (LPS_NBSY | LPS_NACK | LPS_OUT | LPS_SEL | LPS_NERR)
+#define NOT_READY(ppbus) ((ppb_rstr(ppbus)^LPS_INVERT)&LPS_MASK)
+
+#define MAX_SLEEP (hz*5) /* Timeout while waiting for device ready */
+#define MAX_SPIN 20 /* Max delay for device ready in usecs */
+
+
+static d_open_t lptopen;
+static d_close_t lptclose;
+static d_write_t lptwrite;
+static d_read_t lptread;
+static d_ioctl_t lptioctl;
+
+#define CDEV_MAJOR 16
+static struct cdevsw lpt_cdevsw = {
+ /* open */ lptopen,
+ /* close */ lptclose,
+ /* read */ lptread,
+ /* write */ lptwrite,
+ /* ioctl */ lptioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ LPT_NAME,
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+static int
+lpt_request_ppbus(device_t dev, int how)
+{
+ device_t ppbus = device_get_parent(dev);
+ struct lpt_data *sc = DEVTOSOFTC(dev);
+ int error;
+
+ if (sc->sc_state & HAVEBUS)
+ return (0);
+
+ /* we have the bus only if the request succeded */
+ if ((error = ppb_request_bus(ppbus, dev, how)) == 0)
+ sc->sc_state |= HAVEBUS;
+
+ return (error);
+}
+
+static int
+lpt_release_ppbus(device_t dev)
+{
+ device_t ppbus = device_get_parent(dev);
+ struct lpt_data *sc = DEVTOSOFTC(dev);
+ int error = 0;
+
+ if ((error = ppb_release_bus(ppbus, dev)) == 0)
+ sc->sc_state &= ~HAVEBUS;
+
+ return (error);
+}
+
+/*
+ * Internal routine to lptprobe to do port tests of one byte value
+ */
+static int
+lpt_port_test(device_t ppbus, u_char data, u_char mask)
+{
+ int temp, timeout;
+
+ data = data & mask;
+ ppb_wdtr(ppbus, data);
+ timeout = 10000;
+ do {
+ DELAY(10);
+ temp = ppb_rdtr(ppbus) & mask;
+ }
+ while (temp != data && --timeout);
+ lprintf(("out=%x\tin=%x\ttout=%d\n", data, temp, timeout));
+ return (temp == data);
+}
+
+/*
+ * Probe simplified by replacing multiple loops with a hardcoded
+ * test pattern - 1999/02/08 des@freebsd.org
+ *
+ * New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94
+ * Based partially on Rod Grimes' printer probe
+ *
+ * Logic:
+ * 1) If no port address was given, use the bios detected ports
+ * and autodetect what ports the printers are on.
+ * 2) Otherwise, probe the data port at the address given,
+ * using the method in Rod Grimes' port probe.
+ * (Much code ripped off directly from Rod's probe.)
+ *
+ * Comments from Rod's probe:
+ * Logic:
+ * 1) You should be able to write to and read back the same value
+ * to the data port. Do an alternating zeros, alternating ones,
+ * walking zero, and walking one test to check for stuck bits.
+ *
+ * 2) You should be able to write to and read back the same value
+ * to the control port lower 5 bits, the upper 3 bits are reserved
+ * per the IBM PC technical reference manauls and different boards
+ * do different things with them. Do an alternating zeros, alternating
+ * ones, walking zero, and walking one test to check for stuck bits.
+ *
+ * Some printers drag the strobe line down when the are powered off
+ * so this bit has been masked out of the control port test.
+ *
+ * XXX Some printers may not like a fast pulse on init or strobe, I
+ * don't know at this point, if that becomes a problem these bits
+ * should be turned off in the mask byte for the control port test.
+ *
+ * We are finally left with a mask of 0x14, due to some printers
+ * being adamant about holding other bits high ........
+ *
+ * Before probing the control port, we write a 0 to the data port -
+ * If not, some printers chuck out garbage when the strobe line
+ * gets toggled.
+ *
+ * 3) Set the data and control ports to a value of 0
+ *
+ * This probe routine has been tested on Epson Lx-800, HP LJ3P,
+ * Epson FX-1170 and C.Itoh 8510RM
+ * printers.
+ * Quick exit on fail added.
+ */
+static int
+lpt_detect(device_t dev)
+{
+ device_t ppbus = device_get_parent(dev);
+
+ static u_char testbyte[18] = {
+ 0x55, /* alternating zeros */
+ 0xaa, /* alternating ones */
+ 0xfe, 0xfd, 0xfb, 0xf7,
+ 0xef, 0xdf, 0xbf, 0x7f, /* walking zero */
+ 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 /* walking one */
+ };
+ int i, error, status;
+
+ status = 1; /* assume success */
+
+ if ((error = lpt_request_ppbus(dev, PPB_DONTWAIT))) {
+ printf(LPT_NAME ": cannot alloc ppbus (%d)!\n", error);
+ status = 0;
+ goto end_probe;
+ }
+
+ for (i = 0; i < 18 && status; i++)
+ if (!lpt_port_test(ppbus, testbyte[i], 0xff)) {
+ status = 0;
+ goto end_probe;
+ }
+
+end_probe:
+ /* write 0's to control and data ports */
+ ppb_wdtr(ppbus, 0);
+ ppb_wctr(ppbus, 0);
+
+ lpt_release_ppbus(dev);
+
+ return (status);
+}
+
+static void
+lpt_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, LPT_NAME, -1);
+}
+
+/*
+ * lpt_probe()
+ */
+static int
+lpt_probe(device_t dev)
+{
+ struct lpt_data *sc;
+
+ sc = DEVTOSOFTC(dev);
+ bzero(sc, sizeof(struct lpt_data));
+
+ /*
+ * Now, try to detect the printer.
+ */
+ if (!lpt_detect(dev))
+ return (ENXIO);
+
+ device_set_desc(dev, "Printer");
+
+ return (0);
+}
+
+static int
+lpt_attach(device_t dev)
+{
+ device_t ppbus = device_get_parent(dev);
+ struct lpt_data *sc = DEVTOSOFTC(dev);
+ int zero = 0, unit = device_get_unit(dev);
+ int error;
+ intptr_t irq;
+
+ sc->sc_primed = 0; /* not primed yet */
+
+ if ((error = lpt_request_ppbus(dev, PPB_DONTWAIT))) {
+ printf(LPT_NAME ": cannot alloc ppbus (%d)!\n", error);
+ return (0);
+ }
+
+ ppb_wctr(ppbus, LPC_NINIT);
+
+ /* check if we can use interrupt, should be done by ppc stuff */
+ lprintf(("oldirq %x\n", sc->sc_irq));
+
+ /* retrieve the ppbus irq */
+ BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
+
+ if (irq > 0) {
+ /* declare our interrupt handler */
+ sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &zero, irq, irq, 1, RF_SHAREABLE);
+ }
+ if (sc->intr_resource) {
+ sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
+ device_printf(dev, "Interrupt-driven port\n");
+ } else {
+ sc->sc_irq = 0;
+ device_printf(dev, "Polled port\n");
+ }
+ lprintf(("irq %x %x\n", (int)irq, sc->sc_irq));
+
+ lpt_release_ppbus(dev);
+
+ make_dev(&lpt_cdevsw, unit,
+ UID_ROOT, GID_WHEEL, 0600, LPT_NAME "%d", unit);
+ make_dev(&lpt_cdevsw, unit | LP_BYPASS,
+ UID_ROOT, GID_WHEEL, 0600, LPT_NAME "%d.ctl", unit);
+ return (0);
+}
+
+static void
+lptout(void *arg)
+{
+ device_t dev = (device_t)arg;
+ struct lpt_data *sc = DEVTOSOFTC(dev);
+#ifdef LPT_DEBUG
+ device_t ppbus = device_get_parent(dev);
+#endif
+
+ lprintf(("T %x ", ppb_rstr(ppbus)));
+ if (sc->sc_state & OPEN) {
+ sc->sc_backoff++;
+ if (sc->sc_backoff > hz/LPTOUTMAX)
+ sc->sc_backoff = sc->sc_backoff > hz/LPTOUTMAX;
+ timeout(lptout, (caddr_t)dev, sc->sc_backoff);
+ } else
+ sc->sc_state &= ~TOUT;
+
+ if (sc->sc_state & EERROR)
+ sc->sc_state &= ~EERROR;
+
+ /*
+ * Avoid possible hangs due to missed interrupts
+ */
+ if (sc->sc_xfercnt) {
+ lptintr(dev);
+ } else {
+ sc->sc_state &= ~OBUSY;
+ wakeup((caddr_t)dev);
+ }
+}
+
+/*
+ * lptopen -- reset the printer, then wait until it's selected and not busy.
+ * If LP_BYPASS flag is selected, then we do not try to select the
+ * printer -- this is just used for passing ioctls.
+ */
+
+static int
+lptopen(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ int s;
+ int trys, err;
+ u_int unit = LPTUNIT(minor(dev));
+ struct lpt_data *sc = UNITOSOFTC(unit);
+ device_t lptdev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(lptdev);
+
+ if (!sc)
+ return (ENXIO);
+
+ if (sc->sc_state) {
+ lprintf((LPT_NAME ": still open %x\n", sc->sc_state));
+ return(EBUSY);
+ } else
+ sc->sc_state |= LPTINIT;
+
+ sc->sc_flags = LPTFLAGS(minor(dev));
+
+ /* Check for open with BYPASS flag set. */
+ if (sc->sc_flags & LP_BYPASS) {
+ sc->sc_state = OPEN;
+ return(0);
+ }
+
+ /* request the ppbus only if we don't have it already */
+ if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0) {
+ /* give it a chance to try later */
+ sc->sc_state = 0;
+ return (err);
+ }
+
+ s = spltty();
+ lprintf((LPT_NAME " flags 0x%x\n", sc->sc_flags));
+
+ /* set IRQ status according to ENABLE_IRQ flag
+ */
+ if (sc->sc_irq & LP_ENABLE_IRQ)
+ sc->sc_irq |= LP_USE_IRQ;
+ else
+ sc->sc_irq &= ~LP_USE_IRQ;
+
+ /* init printer */
+ if ((sc->sc_flags & LP_NO_PRIME) == 0) {
+ if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {
+ ppb_wctr(ppbus, 0);
+ sc->sc_primed++;
+ DELAY(500);
+ }
+ }
+
+ ppb_wctr(ppbus, LPC_SEL|LPC_NINIT);
+
+ /* wait till ready (printer running diagnostics) */
+ trys = 0;
+ do {
+ /* ran out of waiting for the printer */
+ if (trys++ >= LPINITRDY*4) {
+ splx(s);
+ sc->sc_state = 0;
+ lprintf(("status %x\n", ppb_rstr(ppbus)));
+
+ lpt_release_ppbus(lptdev);
+ return (EBUSY);
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep((caddr_t)lptdev, LPPRI|PCATCH, "lptinit", hz/4) !=
+ EWOULDBLOCK) {
+ sc->sc_state = 0;
+ splx(s);
+
+ lpt_release_ppbus(lptdev);
+ return (EBUSY);
+ }
+
+ /* is printer online and ready for output */
+ } while ((ppb_rstr(ppbus) &
+ (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR));
+
+ sc->sc_control = LPC_SEL|LPC_NINIT;
+ if (sc->sc_flags & LP_AUTOLF)
+ sc->sc_control |= LPC_AUTOL;
+
+ /* enable interrupt if interrupt-driven */
+ if (sc->sc_irq & LP_USE_IRQ)
+ sc->sc_control |= LPC_ENA;
+
+ ppb_wctr(ppbus, sc->sc_control);
+
+ sc->sc_state = OPEN;
+ sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
+ sc->sc_statbuf = malloc(BUFSTATSIZE, M_DEVBUF, M_WAITOK);
+ sc->sc_xfercnt = 0;
+ splx(s);
+
+ /* release the ppbus */
+ lpt_release_ppbus(lptdev);
+
+ /* only use timeout if using interrupt */
+ lprintf(("irq %x\n", sc->sc_irq));
+ if (sc->sc_irq & LP_USE_IRQ) {
+ sc->sc_state |= TOUT;
+ timeout(lptout, (caddr_t)lptdev,
+ (sc->sc_backoff = hz/LPTOUTINITIAL));
+ }
+
+ lprintf(("opened.\n"));
+ return(0);
+}
+
+/*
+ * lptclose -- close the device, free the local line buffer.
+ *
+ * Check for interrupted write call added.
+ */
+
+static int
+lptclose(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ u_int unit = LPTUNIT(minor(dev));
+ struct lpt_data *sc = UNITOSOFTC(unit);
+ device_t lptdev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(lptdev);
+ int err;
+
+ if(sc->sc_flags & LP_BYPASS)
+ goto end_close;
+
+ if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0)
+ return (err);
+
+ sc->sc_state &= ~OPEN;
+
+ /* if the last write was interrupted, don't complete it */
+ if((!(sc->sc_state & INTERRUPTED)) && (sc->sc_irq & LP_USE_IRQ))
+ while ((ppb_rstr(ppbus) &
+ (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep((caddr_t)lptdev, LPPRI|PCATCH,
+ "lpclose", hz) != EWOULDBLOCK)
+ break;
+
+ ppb_wctr(ppbus, LPC_NINIT);
+ free(sc->sc_inbuf, M_DEVBUF);
+ free(sc->sc_statbuf, M_DEVBUF);
+
+end_close:
+ /* release the bus anyway
+ * unregistration of interrupt forced by release
+ */
+ lpt_release_ppbus(lptdev);
+
+ sc->sc_state = 0;
+ sc->sc_xfercnt = 0;
+ lprintf(("closed.\n"));
+ return(0);
+}
+
+/*
+ * lpt_pushbytes()
+ * Workhorse for actually spinning and writing bytes to printer
+ * Derived from lpa.c
+ * Originally by ?
+ *
+ * This code is only used when we are polling the port
+ */
+static int
+lpt_pushbytes(device_t dev)
+{
+ struct lpt_data *sc = DEVTOSOFTC(dev);
+ device_t ppbus = device_get_parent(dev);
+ int spin, err, tic;
+ char ch;
+
+ lprintf(("p"));
+ /* loop for every character .. */
+ while (sc->sc_xfercnt > 0) {
+ /* printer data */
+ ch = *(sc->sc_cp);
+ sc->sc_cp++;
+ sc->sc_xfercnt--;
+
+ /*
+ * Wait for printer ready.
+ * Loop 20 usecs testing BUSY bit, then sleep
+ * for exponentially increasing timeout. (vak)
+ */
+ for (spin = 0; NOT_READY(ppbus) && spin < MAX_SPIN; ++spin)
+ DELAY(1); /* XXX delay is NOT this accurate! */
+ if (spin >= MAX_SPIN) {
+ tic = 0;
+ while (NOT_READY(ppbus)) {
+ /*
+ * Now sleep, every cycle a
+ * little longer ..
+ */
+ tic = tic + tic + 1;
+ /*
+ * But no more than 10 seconds. (vak)
+ */
+ if (tic > MAX_SLEEP)
+ tic = MAX_SLEEP;
+ err = tsleep((caddr_t)dev, LPPRI,
+ LPT_NAME "poll", tic);
+ if (err != EWOULDBLOCK) {
+ return (err);
+ }
+ }
+ }
+
+ /* output data */
+ ppb_wdtr(ppbus, ch);
+ /* strobe */
+ ppb_wctr(ppbus, sc->sc_control|LPC_STB);
+ ppb_wctr(ppbus, sc->sc_control);
+
+ }
+ return(0);
+}
+
+/*
+ * lptread --retrieve printer status in IEEE1284 NIBBLE mode
+ */
+
+static int
+lptread(dev_t dev, struct uio *uio, int ioflag)
+{
+ u_int unit = LPTUNIT(minor(dev));
+ struct lpt_data *sc = UNITOSOFTC(unit);
+ device_t lptdev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(lptdev);
+ int error = 0, len;
+
+ if (sc->sc_flags & LP_BYPASS) {
+ /* we can't do reads in bypass mode */
+ return (EPERM);
+ }
+
+ if ((error = ppb_1284_negociate(ppbus, PPB_NIBBLE, 0)))
+ return (error);
+
+ /* read data in an other buffer, read/write may be simultaneous */
+ len = 0;
+ while (uio->uio_resid) {
+ if ((error = ppb_1284_read(ppbus, PPB_NIBBLE,
+ sc->sc_statbuf, min(BUFSTATSIZE,
+ uio->uio_resid), &len))) {
+ goto error;
+ }
+
+ if (!len)
+ goto error; /* no more data */
+
+ if ((error = uiomove(sc->sc_statbuf, len, uio)))
+ goto error;
+ }
+
+error:
+ ppb_1284_terminate(ppbus);
+ return (error);
+}
+
+/*
+ * lptwrite --copy a line from user space to a local buffer, then call
+ * putc to get the chars moved to the output queue.
+ *
+ * Flagging of interrupted write added.
+ */
+
+static int
+lptwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+ register unsigned n;
+ int err;
+ u_int unit = LPTUNIT(minor(dev));
+ struct lpt_data *sc = UNITOSOFTC(unit);
+ device_t lptdev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(lptdev);
+
+ if(sc->sc_flags & LP_BYPASS) {
+ /* we can't do writes in bypass mode */
+ return(EPERM);
+ }
+
+ /* request the ppbus only if we don't have it already */
+ /* XXX interrupt registration?! */
+ if ((err = lpt_request_ppbus(lptdev, PPB_WAIT|PPB_INTR)) != 0)
+ return (err);
+
+ /* if interrupts are working, register the handler */
+ if (sc->sc_irq & LP_USE_IRQ) {
+ /* register our interrupt handler */
+ err = BUS_SETUP_INTR(ppbus, lptdev, sc->intr_resource,
+ INTR_TYPE_TTY, lpt_intr, lptdev,
+ &sc->intr_cookie);
+ if (err) {
+ device_printf(lptdev, "handler registration failed, polled mode.\n");
+ sc->sc_irq &= ~LP_USE_IRQ;
+ }
+ }
+
+ sc->sc_state &= ~INTERRUPTED;
+ while ((n = min(BUFSIZE, uio->uio_resid)) != 0) {
+ sc->sc_cp = sc->sc_inbuf;
+ uiomove(sc->sc_cp, n, uio);
+ sc->sc_xfercnt = n ;
+
+ if (sc->sc_irq & LP_ENABLE_EXT) {
+ /* try any extended mode */
+ err = ppb_write(ppbus, sc->sc_cp,
+ sc->sc_xfercnt, 0);
+ switch (err) {
+ case 0:
+ /* if not all data was sent, we could rely
+ * on polling for the last bytes */
+ sc->sc_xfercnt = 0;
+ break;
+ case EINTR:
+ sc->sc_state |= INTERRUPTED;
+ return(err);
+ case EINVAL:
+ /* advanced mode not avail */
+ log(LOG_NOTICE, LPT_NAME "%d: advanced mode not avail, polling\n", unit);
+ break;
+ default:
+ return(err);
+ }
+ } else while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {
+ lprintf(("i"));
+ /* if the printer is ready for a char, */
+ /* give it one */
+ if ((sc->sc_state & OBUSY) == 0){
+ lprintf(("\nC %d. ", sc->sc_xfercnt));
+ lptintr(lptdev);
+ }
+ lprintf(("W "));
+ if (sc->sc_state & OBUSY)
+ if ((err = tsleep((caddr_t)lptdev,
+ LPPRI|PCATCH, LPT_NAME "write", 0))) {
+ sc->sc_state |= INTERRUPTED;
+ return(err);
+ }
+ }
+
+ /* check to see if we must do a polled write */
+ if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) {
+ lprintf(("p"));
+
+ err = lpt_pushbytes(lptdev);
+
+ if (err)
+ return(err);
+ }
+ }
+
+ /* we have not been interrupted, release the ppbus */
+ lpt_release_ppbus(lptdev);
+
+ return(0);
+}
+
+/*
+ * lpt_intr -- handle printer interrupts which occur when the printer is
+ * ready to accept another char.
+ *
+ * do checking for interrupted write call.
+ */
+
+static void
+lpt_intr(void *arg)
+{
+ device_t lptdev = (device_t)arg;
+ device_t ppbus = device_get_parent(lptdev);
+ struct lpt_data *sc = DEVTOSOFTC(lptdev);
+ int sts = 0;
+ int i;
+
+ /* we must own the bus to use it */
+ if ((sc->sc_state & HAVEBUS) == 0)
+ return;
+
+ /*
+ * Is printer online and ready for output?
+ *
+ * Avoid falling back to lptout() too quickly. First spin-loop
+ * to see if the printer will become ready ``really soon now''.
+ */
+ for (i = 0; i < 100 &&
+ ((sts=ppb_rstr(ppbus)) & RDY_MASK) != LP_READY; i++) ;
+
+ if ((sts & RDY_MASK) == LP_READY) {
+ sc->sc_state = (sc->sc_state | OBUSY) & ~EERROR;
+ sc->sc_backoff = hz/LPTOUTINITIAL;
+
+ if (sc->sc_xfercnt) {
+ /* send char */
+ /*lprintf(("%x ", *sc->sc_cp)); */
+ ppb_wdtr(ppbus, *sc->sc_cp++) ;
+ ppb_wctr(ppbus, sc->sc_control|LPC_STB);
+ /* DELAY(X) */
+ ppb_wctr(ppbus, sc->sc_control);
+
+ /* any more data for printer */
+ if(--(sc->sc_xfercnt) > 0) return;
+ }
+
+ /*
+ * No more data waiting for printer.
+ * Wakeup is not done if write call was not interrupted.
+ */
+ sc->sc_state &= ~OBUSY;
+
+ if(!(sc->sc_state & INTERRUPTED))
+ wakeup((caddr_t)lptdev);
+ lprintf(("w "));
+ return;
+ } else { /* check for error */
+ if(((sts & (LPS_NERR | LPS_OUT) ) != LPS_NERR) &&
+ (sc->sc_state & OPEN))
+ sc->sc_state |= EERROR;
+ /* lptout() will jump in and try to restart. */
+ }
+ lprintf(("sts %x ", sts));
+}
+
+static void
+lptintr(device_t dev)
+{
+ /* call the interrupt at required spl level */
+ int s = spltty();
+
+ lpt_intr(dev);
+
+ splx(s);
+ return;
+}
+
+static int
+lptioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
+{
+ int error = 0;
+ u_int unit = LPTUNIT(minor(dev));
+ struct lpt_data *sc = UNITOSOFTC(unit);
+ u_char old_sc_irq; /* old printer IRQ status */
+
+ switch (cmd) {
+ case LPT_IRQ :
+ if(sc->sc_irq & LP_HAS_IRQ) {
+ /*
+ * NOTE:
+ * If the IRQ status is changed,
+ * this will only be visible on the
+ * next open.
+ *
+ * If interrupt status changes,
+ * this gets syslog'd.
+ */
+ old_sc_irq = sc->sc_irq;
+ switch(*(int*)data) {
+ case 0:
+ sc->sc_irq &= (~LP_ENABLE_IRQ);
+ break;
+ case 1:
+ sc->sc_irq &= (~LP_ENABLE_EXT);
+ sc->sc_irq |= LP_ENABLE_IRQ;
+ break;
+ case 2:
+ /* classic irq based transfer and advanced
+ * modes are in conflict
+ */
+ sc->sc_irq &= (~LP_ENABLE_IRQ);
+ sc->sc_irq |= LP_ENABLE_EXT;
+ break;
+ case 3:
+ sc->sc_irq &= (~LP_ENABLE_EXT);
+ break;
+ default:
+ break;
+ }
+
+ if (old_sc_irq != sc->sc_irq )
+ log(LOG_NOTICE, LPT_NAME "%d: switched to %s %s mode\n",
+ unit,
+ (sc->sc_irq & LP_ENABLE_IRQ)?
+ "interrupt-driven":"polled",
+ (sc->sc_irq & LP_ENABLE_EXT)?
+ "extended":"standard");
+ } else /* polled port */
+ error = EOPNOTSUPP;
+ break;
+ default:
+ error = ENODEV;
+ }
+
+ return(error);
+}
+
+static device_method_t lpt_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, lpt_identify),
+ DEVMETHOD(device_probe, lpt_probe),
+ DEVMETHOD(device_attach, lpt_attach),
+
+ { 0, 0 }
+};
+
+static driver_t lpt_driver = {
+ LPT_NAME,
+ lpt_methods,
+ sizeof(struct lpt_data),
+};
+
+DRIVER_MODULE(lpt, ppbus, lpt_driver, lpt_devclass, 0, 0);
diff --git a/sys/dev/ppbus/lpt.h b/sys/dev/ppbus/lpt.h
new file mode 100644
index 0000000..9075b4e
--- /dev/null
+++ b/sys/dev/ppbus/lpt.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Copyright (c) 1997, 1999 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Distantly from :
+ * @(#)lptreg.h 1.1 (Berkeley) 12/19/90
+ * Id: lptreg.h,v 1.6 1997/02/22 09:36:52 peter Exp
+ * From Id: nlpt.h,v 1.3 1999/01/10 12:04:54 nsouch Exp
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * AT Parallel Port (for lineprinter)
+ * Interface port and bit definitions
+ * Written by William Jolitz 12/18/90
+ * Copyright (C) William Jolitz 1990
+ */
+
+#ifndef __LPT_H
+#define __LPT_H
+
+/* machine independent definitions, it shall only depend on the ppbus
+ * parallel port model */
+
+ /* PIN */
+#define LPS_NERR 0x08 /* 15 printer no error */
+#define LPS_SEL 0x10 /* 13 printer selected */
+#define LPS_OUT 0x20 /* 12 printer out of paper */
+#define LPS_NACK 0x40 /* 10 printer no ack of data */
+#define LPS_NBSY 0x80 /* 11 printer busy */
+
+#define LPC_STB 0x01 /* 1 strobe data to printer */
+#define LPC_AUTOL 0x02 /* 14 automatic linefeed */
+#define LPC_NINIT 0x04 /* 16 initialize printer */
+#define LPC_SEL 0x08 /* 17 printer selected */
+#define LPC_ENA 0x10 /* - enable IRQ */
+
+#endif
diff --git a/sys/dev/ppbus/lptio.h b/sys/dev/ppbus/lptio.h
new file mode 100644
index 0000000..8458bc6
--- /dev/null
+++ b/sys/dev/ppbus/lptio.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 1994 Geoffrey M. Rehmet
+ *
+ * This program is free software; you may redistribute it and/or
+ * modify it, provided that it retain the above copyright notice
+ * and the following disclaimer.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Geoff Rehmet, Rhodes University, South Africa <csgr@cs.ru.ac.za>
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_PPBUS_LPT_H_
+#define _DEV_PPBUS_LPT_H_
+
+#include <sys/ioccom.h>
+
+#define LPT_IRQ _IOW('p', 1, long) /* set interrupt status */
+
+#endif /* !_DEV_PPBUS_LPT_H_ */
diff --git a/sys/dev/ppbus/pcfclock.c b/sys/dev/ppbus/pcfclock.c
new file mode 100644
index 0000000..bd49ebb
--- /dev/null
+++ b/sys/dev/ppbus/pcfclock.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2000 Sascha Schumann. 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 SASCHA SCHUMANN ``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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_pcfclock.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+#include <dev/ppbus/ppbio.h>
+
+#include "ppbus_if.h"
+
+#define PCFCLOCK_NAME "pcfclock"
+
+struct pcfclock_data {
+ int count;
+};
+
+#define DEVTOSOFTC(dev) \
+ ((struct pcfclock_data *)device_get_softc(dev))
+#define UNITOSOFTC(unit) \
+ ((struct pcfclock_data *)devclass_get_softc(pcfclock_devclass, (unit)))
+#define UNITODEVICE(unit) \
+ (devclass_get_device(pcfclock_devclass, (unit)))
+
+static devclass_t pcfclock_devclass;
+
+static d_open_t pcfclock_open;
+static d_close_t pcfclock_close;
+static d_read_t pcfclock_read;
+
+#define CDEV_MAJOR 140
+static struct cdevsw pcfclock_cdevsw = {
+ /* open */ pcfclock_open,
+ /* close */ pcfclock_close,
+ /* read */ pcfclock_read,
+ /* write */ nowrite,
+ /* ioctl */ noioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ PCFCLOCK_NAME,
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+#ifndef PCFCLOCK_MAX_RETRIES
+#define PCFCLOCK_MAX_RETRIES 10
+#endif
+
+#define AFC_HI 0
+#define AFC_LO AUTOFEED
+
+/* AUTO FEED is used as clock */
+#define AUTOFEED_CLOCK(val) \
+ ctr = (ctr & ~(AUTOFEED)) ^ (val); ppb_wctr(ppbus, ctr)
+
+/* SLCT is used as clock */
+#define CLOCK_OK \
+ ((ppb_rstr(ppbus) & SELECT) == (i & 1 ? SELECT : 0))
+
+/* PE is used as data */
+#define BIT_SET (ppb_rstr(ppbus)&PERROR)
+
+/* the first byte sent as reply must be 00001001b */
+#define PCFCLOCK_CORRECT_SYNC(buf) (buf[0] == 9)
+
+#define NR(buf, off) (buf[off+1]*10+buf[off])
+
+/* check for correct input values */
+#define PCFCLOCK_CORRECT_FORMAT(buf) (\
+ NR(buf, 14) <= 99 && \
+ NR(buf, 12) <= 12 && \
+ NR(buf, 10) <= 31 && \
+ NR(buf, 6) <= 23 && \
+ NR(buf, 4) <= 59 && \
+ NR(buf, 2) <= 59)
+
+#define PCFCLOCK_BATTERY_STATUS_LOW(buf) (buf[8] & 4)
+
+#define PCFCLOCK_CMD_TIME 0 /* send current time */
+#define PCFCLOCK_CMD_COPY 7 /* copy received signal to PC */
+
+static void
+pcfclock_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, PCFCLOCK_NAME, -1);
+}
+
+static int
+pcfclock_probe(device_t dev)
+{
+ struct pcfclock_data *sc;
+
+ device_set_desc(dev, "PCF-1.0");
+
+ sc = DEVTOSOFTC(dev);
+ bzero(sc, sizeof(struct pcfclock_data));
+
+ return (0);
+}
+
+static int
+pcfclock_attach(device_t dev)
+{
+ int unit;
+
+ unit = device_get_unit(dev);
+
+ make_dev(&pcfclock_cdevsw, unit,
+ UID_ROOT, GID_WHEEL, 0444, PCFCLOCK_NAME "%d", unit);
+
+ return (0);
+}
+
+static int
+pcfclock_open(dev_t dev, int flag, int fms, struct thread *td)
+{
+ u_int unit = minor(dev);
+ struct pcfclock_data *sc = UNITOSOFTC(unit);
+ device_t pcfclockdev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(pcfclockdev);
+ int res;
+
+ if (!sc)
+ return (ENXIO);
+
+ if ((res = ppb_request_bus(ppbus, pcfclockdev,
+ (flag & O_NONBLOCK) ? PPB_DONTWAIT : PPB_WAIT)))
+ return (res);
+
+ sc->count++;
+
+ return (0);
+}
+
+static int
+pcfclock_close(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ u_int unit = minor(dev);
+ struct pcfclock_data *sc = UNITOSOFTC(unit);
+ device_t pcfclockdev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(pcfclockdev);
+
+ sc->count--;
+ if (sc->count == 0)
+ ppb_release_bus(ppbus, pcfclockdev);
+
+ return (0);
+}
+
+static void
+pcfclock_write_cmd(dev_t dev, unsigned char command)
+{
+ u_int unit = minor(dev);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ unsigned char ctr = 14;
+ char i;
+
+ for (i = 0; i <= 7; i++) {
+ ppb_wdtr(ppbus, i);
+ AUTOFEED_CLOCK(i & 1 ? AFC_HI : AFC_LO);
+ DELAY(3000);
+ }
+ ppb_wdtr(ppbus, command);
+ AUTOFEED_CLOCK(AFC_LO);
+ DELAY(3000);
+ AUTOFEED_CLOCK(AFC_HI);
+}
+
+static void
+pcfclock_display_data(dev_t dev, char buf[18])
+{
+ u_int unit = minor(dev);
+#ifdef PCFCLOCK_VERBOSE
+ int year;
+
+ year = NR(buf, 14);
+ if (year < 70)
+ year += 100;
+
+ printf(PCFCLOCK_NAME "%d: %02d.%02d.%4d %02d:%02d:%02d, "
+ "battery status: %s\n",
+ unit,
+ NR(buf, 10), NR(buf, 12), 1900 + year,
+ NR(buf, 6), NR(buf, 4), NR(buf, 2),
+ PCFCLOCK_BATTERY_STATUS_LOW(buf) ? "LOW" : "ok");
+#else
+ if (PCFCLOCK_BATTERY_STATUS_LOW(buf))
+ printf(PCFCLOCK_NAME "%d: BATTERY STATUS LOW ON\n",
+ unit);
+#endif
+}
+
+static int
+pcfclock_read_data(dev_t dev, char *buf, ssize_t bits)
+{
+ u_int unit = minor(dev);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ int i;
+ char waitfor;
+ int offset;
+
+ /* one byte per four bits */
+ bzero(buf, ((bits + 3) >> 2) + 1);
+
+ waitfor = 100;
+ for (i = 0; i <= bits; i++) {
+ /* wait for clock, maximum (waitfor*100) usec */
+ while(!CLOCK_OK && --waitfor > 0)
+ DELAY(100);
+
+ /* timed out? */
+ if (!waitfor)
+ return (EIO);
+
+ waitfor = 100; /* reload */
+
+ /* give it some time */
+ DELAY(500);
+
+ /* calculate offset into buffer */
+ offset = i >> 2;
+ buf[offset] <<= 1;
+
+ if (BIT_SET)
+ buf[offset] |= 1;
+ }
+
+ return (0);
+}
+
+static int
+pcfclock_read_dev(dev_t dev, char *buf, int maxretries)
+{
+ u_int unit = minor(dev);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ int error = 0;
+
+ ppb_set_mode(ppbus, PPB_COMPATIBLE);
+
+ while (--maxretries > 0) {
+ pcfclock_write_cmd(dev, PCFCLOCK_CMD_TIME);
+ if (pcfclock_read_data(dev, buf, 68))
+ continue;
+
+ if (!PCFCLOCK_CORRECT_SYNC(buf))
+ continue;
+
+ if (!PCFCLOCK_CORRECT_FORMAT(buf))
+ continue;
+
+ break;
+ }
+
+ if (!maxretries)
+ error = EIO;
+
+ return (error);
+}
+
+static int
+pcfclock_read(dev_t dev, struct uio *uio, int ioflag)
+{
+ u_int unit = minor(dev);
+ char buf[18];
+ int error = 0;
+
+ if (uio->uio_resid < 18)
+ return (ERANGE);
+
+ error = pcfclock_read_dev(dev, buf, PCFCLOCK_MAX_RETRIES);
+
+ if (error) {
+ printf(PCFCLOCK_NAME "%d: no PCF found\n", unit);
+ } else {
+ pcfclock_display_data(dev, buf);
+
+ uiomove(buf, 18, uio);
+ }
+
+ return (error);
+}
+
+static device_method_t pcfclock_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, pcfclock_identify),
+ DEVMETHOD(device_probe, pcfclock_probe),
+ DEVMETHOD(device_attach, pcfclock_attach),
+
+ { 0, 0 }
+};
+
+static driver_t pcfclock_driver = {
+ PCFCLOCK_NAME,
+ pcfclock_methods,
+ sizeof(struct pcfclock_data),
+};
+
+DRIVER_MODULE(pcfclock, ppbus, pcfclock_driver, pcfclock_devclass, 0, 0);
diff --git a/sys/dev/ppbus/ppb_1284.c b/sys/dev/ppbus/ppb_1284.c
new file mode 100644
index 0000000..3fdd11b
--- /dev/null
+++ b/sys/dev/ppbus/ppb_1284.c
@@ -0,0 +1,857 @@
+/*-
+ * Copyright (c) 1997 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * General purpose routines for the IEEE1284-1994 Standard
+ */
+
+#include "opt_ppb_1284.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_1284.h>
+
+#include "ppbus_if.h"
+
+#include <dev/ppbus/ppbio.h>
+
+#define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev))
+
+/*
+ * do_1284_wait()
+ *
+ * Wait for the peripherial up to 40ms
+ */
+static int
+do_1284_wait(device_t bus, char mask, char status)
+{
+ return (ppb_poll_bus(bus, 4, mask, status, PPB_NOINTR | PPB_POLL));
+}
+
+static int
+do_peripheral_wait(device_t bus, char mask, char status)
+{
+ return (ppb_poll_bus(bus, 100, mask, status, PPB_NOINTR | PPB_POLL));
+}
+
+#define nibble2char(s) (((s & ~nACK) >> 3) | (~s & nBUSY) >> 4)
+
+/*
+ * ppb_1284_reset_error()
+ *
+ * Unconditionaly reset the error field
+ */
+static int
+ppb_1284_reset_error(device_t bus, int state)
+{
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+
+ ppb->error = PPB_NO_ERROR;
+ ppb->state = state;
+
+ return (0);
+}
+
+/*
+ * ppb_1284_get_state()
+ *
+ * Get IEEE1284 state
+ */
+int
+ppb_1284_get_state(device_t bus)
+{
+ return (DEVTOSOFTC(bus)->state);
+}
+
+/*
+ * ppb_1284_set_state()
+ *
+ * Change IEEE1284 state if no error occured
+ */
+int
+ppb_1284_set_state(device_t bus, int state)
+{
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+
+ /* call ppb_1284_reset_error() if you absolutly want to change
+ * the state from PPB_ERROR to another */
+ if ((ppb->state != PPB_ERROR) &&
+ (ppb->error == PPB_NO_ERROR)) {
+ ppb->state = state;
+ ppb->error = PPB_NO_ERROR;
+ }
+
+ return (0);
+}
+
+static int
+ppb_1284_set_error(device_t bus, int error, int event)
+{
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+
+ /* do not accumulate errors */
+ if ((ppb->error == PPB_NO_ERROR) &&
+ (ppb->state != PPB_ERROR)) {
+ ppb->error = error;
+ ppb->state = PPB_ERROR;
+ }
+
+#ifdef DEBUG_1284
+ printf("ppb1284: error=%d status=0x%x event=%d\n", error,
+ ppb_rstr(bus) & 0xff, event);
+#endif
+
+ return (0);
+}
+
+/*
+ * ppb_request_mode()
+ *
+ * Converts mode+options into ext. value
+ */
+static int
+ppb_request_mode(int mode, int options)
+{
+ int request_mode = 0;
+
+ if (options & PPB_EXTENSIBILITY_LINK) {
+ request_mode = EXT_LINK_1284_NORMAL;
+
+ } else {
+ switch (mode) {
+ case PPB_NIBBLE:
+ request_mode = (options & PPB_REQUEST_ID) ?
+ NIBBLE_1284_REQUEST_ID :
+ NIBBLE_1284_NORMAL;
+ break;
+ case PPB_PS2:
+ request_mode = (options & PPB_REQUEST_ID) ?
+ BYTE_1284_REQUEST_ID :
+ BYTE_1284_NORMAL;
+ break;
+ case PPB_ECP:
+ if (options & PPB_USE_RLE)
+ request_mode = (options & PPB_REQUEST_ID) ?
+ ECP_1284_RLE_REQUEST_ID :
+ ECP_1284_RLE;
+ else
+ request_mode = (options & PPB_REQUEST_ID) ?
+ ECP_1284_REQUEST_ID :
+ ECP_1284_NORMAL;
+ break;
+ case PPB_EPP:
+ request_mode = EPP_1284_NORMAL;
+ break;
+ default:
+ panic("%s: unsupported mode %d\n", __func__, mode);
+ }
+ }
+
+ return (request_mode);
+}
+
+/*
+ * ppb_peripheral_negociate()
+ *
+ * Negociate the peripheral side
+ */
+int
+ppb_peripheral_negociate(device_t bus, int mode, int options)
+{
+ int spin, request_mode, error = 0;
+ char r;
+
+ ppb_set_mode(bus, PPB_COMPATIBLE);
+ ppb_1284_set_state(bus, PPB_PERIPHERAL_NEGOCIATION);
+
+ /* compute ext. value */
+ request_mode = ppb_request_mode(mode, options);
+
+ /* wait host */
+ spin = 10;
+ while (spin-- && (ppb_rstr(bus) & nBUSY))
+ DELAY(1);
+
+ /* check termination */
+ if (!(ppb_rstr(bus) & SELECT) || !spin) {
+ error = ENODEV;
+ goto error;
+ }
+
+ /* Event 4 - read ext. value */
+ r = ppb_rdtr(bus);
+
+ /* nibble mode is not supported */
+ if ((r == (char)request_mode) ||
+ (r == NIBBLE_1284_NORMAL)) {
+
+ /* Event 5 - restore direction bit, no data avail */
+ ppb_wctr(bus, (STROBE | nINIT) & ~(SELECTIN));
+ DELAY(1);
+
+ /* Event 6 */
+ ppb_wctr(bus, (nINIT) & ~(SELECTIN | STROBE));
+
+ if (r == NIBBLE_1284_NORMAL) {
+#ifdef DEBUG_1284
+ printf("R");
+#endif
+ ppb_1284_set_error(bus, PPB_MODE_UNSUPPORTED, 4);
+ error = EINVAL;
+ goto error;
+ } else {
+ ppb_1284_set_state(bus, PPB_PERIPHERAL_IDLE);
+ switch (r) {
+ case BYTE_1284_NORMAL:
+ ppb_set_mode(bus, PPB_BYTE);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_1284
+ printf("A");
+#endif
+ /* negociation succeeds */
+ }
+ } else {
+ /* Event 5 - mode not supported */
+ ppb_wctr(bus, SELECTIN);
+ DELAY(1);
+
+ /* Event 6 */
+ ppb_wctr(bus, (SELECTIN) & ~(STROBE | nINIT));
+ ppb_1284_set_error(bus, PPB_MODE_UNSUPPORTED, 4);
+
+#ifdef DEBUG_1284
+ printf("r");
+#endif
+ error = EINVAL;
+ goto error;
+ }
+
+ return (0);
+
+error:
+ ppb_peripheral_terminate(bus, PPB_WAIT);
+ return (error);
+}
+
+/*
+ * ppb_peripheral_terminate()
+ *
+ * Terminate peripheral transfer side
+ *
+ * Always return 0 in compatible mode
+ */
+int
+ppb_peripheral_terminate(device_t bus, int how)
+{
+ int error = 0;
+
+#ifdef DEBUG_1284
+ printf("t");
+#endif
+
+ ppb_1284_set_state(bus, PPB_PERIPHERAL_TERMINATION);
+
+ /* Event 22 - wait up to host response time (1s) */
+ if ((error = do_peripheral_wait(bus, SELECT | nBUSY, 0))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 22);
+ goto error;
+ }
+
+ /* Event 24 */
+ ppb_wctr(bus, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN));
+
+ /* Event 25 - wait up to host response time (1s) */
+ if ((error = do_peripheral_wait(bus, nBUSY, nBUSY))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 25);
+ goto error;
+ }
+
+ /* Event 26 */
+ ppb_wctr(bus, (SELECTIN | nINIT | STROBE) & ~(AUTOFEED));
+ DELAY(1);
+ /* Event 27 */
+ ppb_wctr(bus, (SELECTIN | nINIT) & ~(STROBE | AUTOFEED));
+
+ /* Event 28 - wait up to host response time (1s) */
+ if ((error = do_peripheral_wait(bus, nBUSY, 0))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 28);
+ goto error;
+ }
+
+error:
+ ppb_set_mode(bus, PPB_COMPATIBLE);
+ ppb_1284_set_state(bus, PPB_FORWARD_IDLE);
+
+ return (0);
+}
+
+/*
+ * byte_peripheral_outbyte()
+ *
+ * Write 1 byte in BYTE mode
+ */
+static int
+byte_peripheral_outbyte(device_t bus, char *buffer, int last)
+{
+ int error = 0;
+
+ /* Event 7 */
+ if ((error = do_1284_wait(bus, nBUSY, nBUSY))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 7);
+ goto error;
+ }
+
+ /* check termination */
+ if (!(ppb_rstr(bus) & SELECT)) {
+ ppb_peripheral_terminate(bus, PPB_WAIT);
+ goto error;
+ }
+
+ /* Event 15 - put byte on data lines */
+#ifdef DEBUG_1284
+ printf("B");
+#endif
+ ppb_wdtr(bus, *buffer);
+
+ /* Event 9 */
+ ppb_wctr(bus, (AUTOFEED | STROBE) & ~(nINIT | SELECTIN));
+
+ /* Event 10 - wait data read */
+ if ((error = do_peripheral_wait(bus, nBUSY, 0))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 16);
+ goto error;
+ }
+
+ /* Event 11 */
+ if (!last) {
+ ppb_wctr(bus, (AUTOFEED) & ~(nINIT | STROBE | SELECTIN));
+ } else {
+ ppb_wctr(bus, (nINIT) & ~(STROBE | SELECTIN | AUTOFEED));
+ }
+
+#if 0
+ /* Event 16 - wait strobe */
+ if ((error = do_peripheral_wait(bus, nACK | nBUSY, 0))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 16);
+ goto error;
+ }
+#endif
+
+ /* check termination */
+ if (!(ppb_rstr(bus) & SELECT)) {
+ ppb_peripheral_terminate(bus, PPB_WAIT);
+ goto error;
+ }
+
+error:
+ return (error);
+}
+
+/*
+ * byte_peripheral_write()
+ *
+ * Write n bytes in BYTE mode
+ */
+int
+byte_peripheral_write(device_t bus, char *buffer, int len, int *sent)
+{
+ int error = 0, i;
+ char r;
+
+ ppb_1284_set_state(bus, PPB_PERIPHERAL_TRANSFER);
+
+ /* wait forever, the remote host is master and should initiate
+ * termination
+ */
+ for (i=0; i<len; i++) {
+ /* force remote nFAULT low to release the remote waiting
+ * process, if any
+ */
+ r = ppb_rctr(bus);
+ ppb_wctr(bus, r & ~nINIT);
+
+#ifdef DEBUG_1284
+ printf("y");
+#endif
+ /* Event 7 */
+ error = ppb_poll_bus(bus, PPB_FOREVER, nBUSY, nBUSY,
+ PPB_INTR);
+
+ if (error && error != EWOULDBLOCK)
+ goto error;
+
+#ifdef DEBUG_1284
+ printf("b");
+#endif
+ if ((error = byte_peripheral_outbyte(bus, buffer+i, (i == len-1))))
+ goto error;
+ }
+error:
+ if (!error)
+ ppb_1284_set_state(bus, PPB_PERIPHERAL_IDLE);
+
+ *sent = i;
+ return (error);
+}
+
+/*
+ * byte_1284_inbyte()
+ *
+ * Read 1 byte in BYTE mode
+ */
+int
+byte_1284_inbyte(device_t bus, char *buffer)
+{
+ int error = 0;
+
+ /* Event 7 - ready to take data (nAUTO low) */
+ ppb_wctr(bus, (PCD | nINIT | AUTOFEED) & ~(STROBE | SELECTIN));
+
+ /* Event 9 - peripheral set nAck low */
+ if ((error = do_1284_wait(bus, nACK, 0))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 9);
+ goto error;
+ }
+
+ /* read the byte */
+ *buffer = ppb_rdtr(bus);
+
+ /* Event 10 - data received, can't accept more */
+ ppb_wctr(bus, (nINIT) & ~(AUTOFEED | STROBE | SELECTIN));
+
+ /* Event 11 - peripheral ack */
+ if ((error = do_1284_wait(bus, nACK, nACK))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 11);
+ goto error;
+ }
+
+ /* Event 16 - strobe */
+ ppb_wctr(bus, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN));
+ DELAY(3);
+ ppb_wctr(bus, (nINIT) & ~(AUTOFEED | STROBE | SELECTIN));
+
+error:
+ return (error);
+}
+
+/*
+ * nibble_1284_inbyte()
+ *
+ * Read 1 byte in NIBBLE mode
+ */
+int
+nibble_1284_inbyte(device_t bus, char *buffer)
+{
+ char nibble[2];
+ int i, error;
+
+ for (i = 0; i < 2; i++) {
+
+ /* Event 7 - ready to take data (nAUTO low) */
+ ppb_wctr(bus, (nINIT | AUTOFEED) & ~(STROBE | SELECTIN));
+
+ /* Event 8 - peripheral writes the first nibble */
+
+ /* Event 9 - peripheral set nAck low */
+ if ((error = do_1284_wait(bus, nACK, 0))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 9);
+ goto error;
+ }
+
+ /* read nibble */
+ nibble[i] = ppb_rstr(bus);
+
+ /* Event 10 - ack, nibble received */
+ ppb_wctr(bus, nINIT & ~(AUTOFEED | STROBE | SELECTIN));
+
+ /* Event 11 - wait ack from peripherial */
+ if ((error = do_1284_wait(bus, nACK, nACK))) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 11);
+ goto error;
+ }
+ }
+
+ *buffer = ((nibble2char(nibble[1]) << 4) & 0xf0) |
+ (nibble2char(nibble[0]) & 0x0f);
+
+error:
+ return (error);
+}
+
+/*
+ * spp_1284_read()
+ *
+ * Read in IEEE1284 NIBBLE/BYTE mode
+ */
+int
+spp_1284_read(device_t bus, int mode, char *buffer, int max, int *read)
+{
+ int error = 0, len = 0;
+ int terminate_after_transfer = 1;
+ int state;
+
+ *read = len = 0;
+
+ state = ppb_1284_get_state(bus);
+
+ switch (state) {
+ case PPB_FORWARD_IDLE:
+ if ((error = ppb_1284_negociate(bus, mode, 0)))
+ return (error);
+ break;
+
+ case PPB_REVERSE_IDLE:
+ terminate_after_transfer = 0;
+ break;
+
+ default:
+ ppb_1284_terminate(bus);
+ if ((error = ppb_1284_negociate(bus, mode, 0)))
+ return (error);
+ break;
+ }
+
+ while ((len < max) && !(ppb_rstr(bus) & (nFAULT))) {
+
+ ppb_1284_set_state(bus, PPB_REVERSE_TRANSFER);
+
+#ifdef DEBUG_1284
+ printf("B");
+#endif
+
+ switch (mode) {
+ case PPB_NIBBLE:
+ /* read a byte, error means no more data */
+ if (nibble_1284_inbyte(bus, buffer+len))
+ goto end_while;
+ break;
+ case PPB_BYTE:
+ if (byte_1284_inbyte(bus, buffer+len))
+ goto end_while;
+ break;
+ default:
+ error = EINVAL;
+ goto end_while;
+ }
+ len ++;
+ }
+end_while:
+
+ if (!error)
+ ppb_1284_set_state(bus, PPB_REVERSE_IDLE);
+
+ *read = len;
+
+ if (terminate_after_transfer || error)
+ ppb_1284_terminate(bus);
+
+ return (error);
+}
+
+/*
+ * ppb_1284_read_id()
+ *
+ */
+int
+ppb_1284_read_id(device_t bus, int mode, char *buffer,
+ int max, int *read)
+{
+ int error = 0;
+
+ /* fill the buffer with 0s */
+ bzero(buffer, max);
+
+ switch (mode) {
+ case PPB_NIBBLE:
+ case PPB_ECP:
+ if ((error = ppb_1284_negociate(bus, PPB_NIBBLE, PPB_REQUEST_ID)))
+ return (error);
+ error = spp_1284_read(bus, PPB_NIBBLE, buffer, max, read);
+ break;
+ case PPB_BYTE:
+ if ((error = ppb_1284_negociate(bus, PPB_BYTE, PPB_REQUEST_ID)))
+ return (error);
+ error = spp_1284_read(bus, PPB_BYTE, buffer, max, read);
+ break;
+ default:
+ panic("%s: unsupported mode %d\n", __func__, mode);
+ }
+
+ ppb_1284_terminate(bus);
+ return (error);
+}
+
+/*
+ * ppb_1284_read()
+ *
+ * IEEE1284 read
+ */
+int
+ppb_1284_read(device_t bus, int mode, char *buffer,
+ int max, int *read)
+{
+ int error = 0;
+
+ switch (mode) {
+ case PPB_NIBBLE:
+ case PPB_BYTE:
+ error = spp_1284_read(bus, mode, buffer, max, read);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (error);
+}
+
+/*
+ * ppb_1284_negociate()
+ *
+ * IEEE1284 negociation phase
+ *
+ * Normal nibble mode or request device id mode (see ppb_1284.h)
+ *
+ * After negociation, nFAULT is low if data is available
+ */
+int
+ppb_1284_negociate(device_t bus, int mode, int options)
+{
+ int error;
+ int request_mode;
+
+#ifdef DEBUG_1284
+ printf("n");
+#endif
+
+ if (ppb_1284_get_state(bus) >= PPB_PERIPHERAL_NEGOCIATION)
+ ppb_peripheral_terminate(bus, PPB_WAIT);
+
+ if (ppb_1284_get_state(bus) != PPB_FORWARD_IDLE)
+ ppb_1284_terminate(bus);
+
+#ifdef DEBUG_1284
+ printf("%d", mode);
+#endif
+
+ /* ensure the host is in compatible mode */
+ ppb_set_mode(bus, PPB_COMPATIBLE);
+
+ /* reset error to catch the actual negociation error */
+ ppb_1284_reset_error(bus, PPB_FORWARD_IDLE);
+
+ /* calculate ext. value */
+ request_mode = ppb_request_mode(mode, options);
+
+ /* default state */
+ ppb_wctr(bus, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED));
+ DELAY(1);
+
+ /* enter negociation phase */
+ ppb_1284_set_state(bus, PPB_NEGOCIATION);
+
+ /* Event 0 - put the exten. value on the data lines */
+ ppb_wdtr(bus, request_mode);
+
+#ifdef PERIPH_1284
+ /* request remote host attention */
+ ppb_wctr(bus, (nINIT | STROBE) & ~(AUTOFEED | SELECTIN));
+ DELAY(1);
+ ppb_wctr(bus, (nINIT) & ~(STROBE | AUTOFEED | SELECTIN));
+#else
+ DELAY(1);
+
+#endif /* !PERIPH_1284 */
+
+ /* Event 1 - enter IEEE1284 mode */
+ ppb_wctr(bus, (nINIT | AUTOFEED) & ~(STROBE | SELECTIN));
+
+#ifdef PERIPH_1284
+ /* ignore the PError line, wait a bit more, remote host's
+ * interrupts don't respond fast enough */
+ if (ppb_poll_bus(bus, 40, nACK | SELECT | nFAULT,
+ SELECT | nFAULT, PPB_NOINTR | PPB_POLL)) {
+ ppb_1284_set_error(bus, PPB_NOT_IEEE1284, 2);
+ error = ENODEV;
+ goto error;
+ }
+#else
+ /* Event 2 - trying IEEE1284 dialog */
+ if (do_1284_wait(bus, nACK | PERROR | SELECT | nFAULT,
+ PERROR | SELECT | nFAULT)) {
+ ppb_1284_set_error(bus, PPB_NOT_IEEE1284, 2);
+ error = ENODEV;
+ goto error;
+ }
+#endif /* !PERIPH_1284 */
+
+ /* Event 3 - latch the ext. value to the peripheral */
+ ppb_wctr(bus, (nINIT | STROBE | AUTOFEED) & ~SELECTIN);
+ DELAY(1);
+
+ /* Event 4 - IEEE1284 device recognized */
+ ppb_wctr(bus, nINIT & ~(SELECTIN | AUTOFEED | STROBE));
+
+ /* Event 6 - waiting for status lines */
+ if (do_1284_wait(bus, nACK, nACK)) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 6);
+ error = EBUSY;
+ goto error;
+ }
+
+ /* Event 7 - quering result consider nACK not to misunderstand
+ * a remote computer terminate sequence */
+ if (options & PPB_EXTENSIBILITY_LINK) {
+
+ /* XXX not fully supported yet */
+ ppb_1284_terminate(bus);
+ return (0);
+
+ }
+ if (request_mode == NIBBLE_1284_NORMAL) {
+ if (do_1284_wait(bus, nACK | SELECT, nACK)) {
+ ppb_1284_set_error(bus, PPB_MODE_UNSUPPORTED, 7);
+ error = ENODEV;
+ goto error;
+ }
+ } else {
+ if (do_1284_wait(bus, nACK | SELECT, SELECT | nACK)) {
+ ppb_1284_set_error(bus, PPB_MODE_UNSUPPORTED, 7);
+ error = ENODEV;
+ goto error;
+ }
+ }
+
+ switch (mode) {
+ case PPB_NIBBLE:
+ case PPB_PS2:
+ /* enter reverse idle phase */
+ ppb_1284_set_state(bus, PPB_REVERSE_IDLE);
+ break;
+ case PPB_ECP:
+ /* negociation ok, now setup the communication */
+ ppb_1284_set_state(bus, PPB_SETUP);
+ ppb_wctr(bus, (nINIT | AUTOFEED) & ~(SELECTIN | STROBE));
+
+#ifdef PERIPH_1284
+ /* ignore PError line */
+ if (do_1284_wait(bus, nACK | SELECT | nBUSY,
+ nACK | SELECT | nBUSY)) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 30);
+ error = ENODEV;
+ goto error;
+ }
+#else
+ if (do_1284_wait(bus, nACK | SELECT | PERROR | nBUSY,
+ nACK | SELECT | PERROR | nBUSY)) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 30);
+ error = ENODEV;
+ goto error;
+ }
+#endif /* !PERIPH_1284 */
+
+ /* ok, the host enters the ForwardIdle state */
+ ppb_1284_set_state(bus, PPB_ECP_FORWARD_IDLE);
+ break;
+ case PPB_EPP:
+ ppb_1284_set_state(bus, PPB_EPP_IDLE);
+ break;
+
+ default:
+ panic("%s: unknown mode (%d)!", __func__, mode);
+ }
+ ppb_set_mode(bus, mode);
+
+ return (0);
+
+error:
+ ppb_1284_terminate(bus);
+
+ return (error);
+}
+
+/*
+ * ppb_1284_terminate()
+ *
+ * IEEE1284 termination phase, return code should ignored since the host
+ * is _always_ in compatible mode after ppb_1284_terminate()
+ */
+int
+ppb_1284_terminate(device_t bus)
+{
+
+#ifdef DEBUG_1284
+ printf("T");
+#endif
+
+ /* do not reset error here to keep the error that
+ * may occured before the ppb_1284_terminate() call */
+ ppb_1284_set_state(bus, PPB_TERMINATION);
+
+#ifdef PERIPH_1284
+ /* request remote host attention */
+ ppb_wctr(bus, (nINIT | STROBE | SELECTIN) & ~(AUTOFEED));
+ DELAY(1);
+#endif /* PERIPH_1284 */
+
+ /* Event 22 - set nSelectin low and nAutoFeed high */
+ ppb_wctr(bus, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED));
+
+ /* Event 24 - waiting for peripheral, Xflag ignored */
+ if (do_1284_wait(bus, nACK | nBUSY | nFAULT, nFAULT)) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 24);
+ goto error;
+ }
+
+ /* Event 25 - set nAutoFd low */
+ ppb_wctr(bus, (nINIT | SELECTIN | AUTOFEED) & ~STROBE);
+
+ /* Event 26 - compatible mode status is set */
+
+ /* Event 27 - peripheral set nAck high */
+ if (do_1284_wait(bus, nACK, nACK)) {
+ ppb_1284_set_error(bus, PPB_TIMEOUT, 27);
+ }
+
+ /* Event 28 - end termination, return to idle phase */
+ ppb_wctr(bus, (nINIT | SELECTIN) & ~(STROBE | AUTOFEED));
+
+error:
+ /* return to compatible mode */
+ ppb_set_mode(bus, PPB_COMPATIBLE);
+ ppb_1284_set_state(bus, PPB_FORWARD_IDLE);
+
+ return (0);
+}
diff --git a/sys/dev/ppbus/ppb_1284.h b/sys/dev/ppbus/ppb_1284.h
new file mode 100644
index 0000000..5c4010f
--- /dev/null
+++ b/sys/dev/ppbus/ppb_1284.h
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1997 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+#ifndef __1284_H
+#define __1284_H
+
+/*
+ * IEEE1284 signals
+ */
+
+/* host driven signals */
+
+#define nHostClk STROBE
+#define Write STROBE
+
+#define nHostBusy AUTOFEED
+#define nHostAck AUTOFEED
+#define DStrb AUTOFEED
+
+#define nReveseRequest nINIT
+
+#define nActive1284 SELECTIN
+#define AStrb SELECTIN
+
+/* peripheral driven signals */
+
+#define nDataAvail nFAULT
+#define nPeriphRequest nFAULT
+
+#define Xflag SELECT
+
+#define AckDataReq PERROR
+#define nAckReverse PERROR
+
+#define nPtrBusy nBUSY
+#define nPeriphAck nBUSY
+#define Wait nBUSY
+
+#define PtrClk nACK
+#define PeriphClk nACK
+#define Intr nACK
+
+/* request mode values */
+#define NIBBLE_1284_NORMAL 0x0
+#define NIBBLE_1284_REQUEST_ID 0x4
+#define BYTE_1284_NORMAL 0x1
+#define BYTE_1284_REQUEST_ID 0x5
+#define ECP_1284_NORMAL 0x10
+#define ECP_1284_REQUEST_ID 0x14
+#define ECP_1284_RLE 0x30
+#define ECP_1284_RLE_REQUEST_ID 0x34
+#define EPP_1284_NORMAL 0x40
+#define EXT_LINK_1284_NORMAL 0x80
+
+/* ieee1284 mode options */
+#define PPB_REQUEST_ID 0x1
+#define PPB_USE_RLE 0x2
+#define PPB_EXTENSIBILITY_LINK 0x4
+
+/* ieee1284 errors */
+#define PPB_NO_ERROR 0
+#define PPB_MODE_UNSUPPORTED 1 /* mode not supported by peripheral */
+#define PPB_NOT_IEEE1284 2 /* not an IEEE1284 compliant periph. */
+#define PPB_TIMEOUT 3 /* timeout */
+#define PPB_INVALID_MODE 4 /* current mode is incorrect */
+
+/* ieee1284 host side states */
+#define PPB_ERROR 0
+#define PPB_FORWARD_IDLE 1
+#define PPB_NEGOCIATION 2
+#define PPB_SETUP 3
+#define PPB_ECP_FORWARD_IDLE 4
+#define PPB_FWD_TO_REVERSE 5
+#define PPB_REVERSE_IDLE 6
+#define PPB_REVERSE_TRANSFER 7
+#define PPB_REVERSE_TO_FWD 8
+#define PPB_EPP_IDLE 9
+#define PPB_TERMINATION 10
+
+/* peripheral side states */
+#define PPB_PERIPHERAL_NEGOCIATION 11
+#define PPB_PERIPHERAL_IDLE 12
+#define PPB_PERIPHERAL_TRANSFER 13
+#define PPB_PERIPHERAL_TERMINATION 14
+
+extern int nibble_1284_inbyte(device_t, char *);
+extern int byte_1284_inbyte(device_t, char *);
+extern int spp_1284_read(device_t, int, char *, int, int *);
+
+extern int ppb_1284_negociate(device_t, int, int);
+extern int ppb_1284_terminate(device_t);
+extern int ppb_1284_read_id(device_t, int, char *, int, int *);
+extern int ppb_1284_read(device_t, int, char *, int, int *);
+extern int ppb_1284_get_state(device_t bus);
+extern int ppb_1284_set_state(device_t bus, int state);
+
+extern int ppb_peripheral_terminate(device_t, int);
+extern int ppb_peripheral_negociate(device_t, int, int);
+extern int byte_peripheral_write(device_t, char *, int, int *);
+
+#endif
diff --git a/sys/dev/ppbus/ppb_base.c b/sys/dev/ppbus/ppb_base.c
new file mode 100644
index 0000000..d74216c
--- /dev/null
+++ b/sys/dev/ppbus/ppb_base.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+
+#include <dev/ppbus/ppbconf.h>
+
+#include "ppbus_if.h"
+
+#include <dev/ppbus/ppbio.h>
+
+#define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev))
+
+/*
+ * ppb_poll_bus()
+ *
+ * Polls the bus
+ *
+ * max is a delay in 10-milliseconds
+ */
+int
+ppb_poll_bus(device_t bus, int max,
+ char mask, char status, int how)
+{
+ int i, j, error;
+ char r;
+
+ /* try at least up to 10ms */
+ for (j = 0; j < ((how & PPB_POLL) ? max : 1); j++) {
+ for (i = 0; i < 10000; i++) {
+ r = ppb_rstr(bus);
+ DELAY(1);
+ if ((r & mask) == status)
+ return (0);
+ }
+ }
+
+ if (!(how & PPB_POLL)) {
+ for (i = 0; max == PPB_FOREVER || i < max-1; i++) {
+ if ((ppb_rstr(bus) & mask) == status)
+ return (0);
+
+ switch (how) {
+ case PPB_NOINTR:
+ /* wait 10 ms */
+ tsleep((caddr_t)bus, PPBPRI, "ppbpoll", hz/100);
+ break;
+
+ case PPB_INTR:
+ default:
+ /* wait 10 ms */
+ if (((error = tsleep((caddr_t)bus, PPBPRI | PCATCH,
+ "ppbpoll", hz/100)) != EWOULDBLOCK) != 0) {
+ return (error);
+ }
+ break;
+ }
+ }
+ }
+
+ return (EWOULDBLOCK);
+}
+
+/*
+ * ppb_get_epp_protocol()
+ *
+ * Return the chipset EPP protocol
+ */
+int
+ppb_get_epp_protocol(device_t bus)
+{
+ uintptr_t protocol;
+
+ BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_EPP_PROTO, &protocol);
+
+ return (protocol);
+}
+
+/*
+ * ppb_get_mode()
+ *
+ */
+int
+ppb_get_mode(device_t bus)
+{
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+
+ /* XXX yet device mode = ppbus mode = chipset mode */
+ return (ppb->mode);
+}
+
+/*
+ * ppb_set_mode()
+ *
+ * Set the operating mode of the chipset, return the previous mode
+ */
+int
+ppb_set_mode(device_t bus, int mode)
+{
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+ int old_mode = ppb_get_mode(bus);
+
+ if (PPBUS_SETMODE(device_get_parent(bus), mode))
+ return -1;
+
+ /* XXX yet device mode = ppbus mode = chipset mode */
+ ppb->mode = (mode & PPB_MASK);
+
+ return (old_mode);
+}
+
+/*
+ * ppb_write()
+ *
+ * Write charaters to the port
+ */
+int
+ppb_write(device_t bus, char *buf, int len, int how)
+{
+ return (PPBUS_WRITE(device_get_parent(bus), buf, len, how));
+}
+
+/*
+ * ppb_reset_epp_timeout()
+ *
+ * Reset the EPP timeout bit in the status register
+ */
+int
+ppb_reset_epp_timeout(device_t bus)
+{
+ return(PPBUS_RESET_EPP(device_get_parent(bus)));
+}
+
+/*
+ * ppb_ecp_sync()
+ *
+ * Wait for the ECP FIFO to be empty
+ */
+int
+ppb_ecp_sync(device_t bus)
+{
+ return (PPBUS_ECP_SYNC(device_get_parent(bus)));
+}
+
+/*
+ * ppb_get_status()
+ *
+ * Read the status register and update the status info
+ */
+int
+ppb_get_status(device_t bus, struct ppb_status *status)
+{
+ register char r;
+
+ r = status->status = ppb_rstr(bus);
+
+ status->timeout = r & TIMEOUT;
+ status->error = !(r & nFAULT);
+ status->select = r & SELECT;
+ status->paper_end = r & PERROR;
+ status->ack = !(r & nACK);
+ status->busy = !(r & nBUSY);
+
+ return (0);
+}
diff --git a/sys/dev/ppbus/ppb_msq.c b/sys/dev/ppbus/ppb_msq.c
new file mode 100644
index 0000000..6ed7ccd
--- /dev/null
+++ b/sys/dev/ppbus/ppb_msq.c
@@ -0,0 +1,337 @@
+/*-
+ * Copyright (c) 1998, 1999 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#include <machine/stdarg.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+
+#include "ppbus_if.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(device_t bus, struct ppb_device *ppbdev, int opcode)
+{
+ int index, epp;
+ struct ppb_xfer *table;
+
+ switch (opcode) {
+ case MS_OP_GET:
+ table = ppbdev->get_xfer;
+ break;
+
+ case MS_OP_PUT:
+ table = ppbdev->put_xfer;
+ break;
+
+ default:
+ panic("%s: unknown opcode (%d)", __func__, opcode);
+ }
+
+ /* retrieve the device operating mode */
+ switch (ppb_get_mode(bus)) {
+ 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(bus))) {
+ case EPP_1_7:
+ index = EPP17_MSQ;
+ break;
+ case EPP_1_9:
+ index = EPP19_MSQ;
+ break;
+ default:
+ panic("%s: unknown EPP protocol (0x%x)!", __func__,
+ epp);
+ }
+ break;
+ case PPB_ECP:
+ index = ECP_MSQ;
+ break;
+ default:
+ panic("%s: unknown mode (%d)", __func__, ppbdev->mode);
+ }
+
+ return (&table[index]);
+}
+
+/*
+ * ppb_MS_init()
+ *
+ * Initialize device dependent submicrosequence of the current mode
+ *
+ */
+int
+ppb_MS_init(device_t bus, device_t dev, struct ppb_microseq *loop, int opcode)
+{
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
+ struct ppb_xfer *xfer = mode2xfer(bus, ppbdev, opcode);
+
+ xfer->loop = loop;
+
+ return (0);
+}
+
+/*
+ * ppb_MS_exec()
+ *
+ * Execute any microsequence opcode - expensive
+ *
+ */
+int
+ppb_MS_exec(device_t bus, device_t 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(bus, dev, msq, ret));
+}
+
+/*
+ * ppb_MS_loop()
+ *
+ * Execute a microseq loop
+ *
+ */
+int
+ppb_MS_loop(device_t bus, device_t dev, struct ppb_microseq *prolog,
+ struct ppb_microseq *body, struct ppb_microseq *epilog,
+ int iter, int *ret)
+{
+ struct ppb_microseq loop_microseq[] = {
+ MS_CALL(0), /* execute prolog */
+
+ MS_SET(MS_UNKNOWN), /* set size of transfer */
+ /* loop: */
+ MS_CALL(0), /* execute body */
+ MS_DBRA(-1 /* loop: */),
+
+ MS_CALL(0), /* 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(bus, 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;
+
+ 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)!",
+ __func__, param);
+
+#if 0
+ printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
+ __func__, 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].i = (int)va_arg(p_list, int);
+ 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)!", __func__,
+ 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(device_t bus, device_t dev, struct ppb_microseq *msq, int *ret)
+{
+ struct ppb_data *ppb = (struct ppb_data *)device_get_softc(bus);
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
+
+ struct ppb_microseq *mi; /* current microinstruction */
+ int 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 (mi ++)
+
+ mi = msq;
+ for (;;) {
+ switch (mi->opcode) {
+ case MS_OP_PUT:
+ case MS_OP_GET:
+
+ /* attempt to choose the best mode for the device */
+ xfer = mode2xfer(bus, ppbdev, mi->opcode);
+
+ /* figure out if we should use ieee1284 code */
+ if (!xfer->loop) {
+ if (mi->opcode == MS_OP_PUT) {
+ if ((error = PPBUS_WRITE(
+ device_get_parent(bus),
+ (char *)mi->arg[0].p,
+ mi->arg[1].i, 0)))
+ goto error;
+
+ INCR_PC;
+ goto next;
+ } else
+ panic("%s: IEEE1284 read not supported", __func__);
+ }
+
+ /* 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(bus, dev, initxfer, &error);
+
+ if (error)
+ goto error;
+
+ /* the xfer microsequence should not contain any
+ * MS_OP_PUT or MS_OP_GET!
+ */
+ ppb_MS_microseq(bus, dev, xfer->loop, &error);
+
+ if (error)
+ goto error;
+
+ INCR_PC;
+ break;
+
+ case MS_OP_RET:
+ if (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 = PPBUS_EXEC_MICROSEQ(
+ device_get_parent(bus), &mi)))
+ goto error;
+ break;
+ }
+ next:
+ continue;
+ }
+error:
+ return (error);
+}
+
diff --git a/sys/dev/ppbus/ppb_msq.h b/sys/dev/ppbus/ppb_msq.h
new file mode 100644
index 0000000..1ee55e5
--- /dev/null
+++ b/sys/dev/ppbus/ppb_msq.h
@@ -0,0 +1,205 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#ifndef __PPB_MSQ_H
+#define __PPB_MSQ_H
+
+/*
+ * Basic definitions
+ */
+
+/* microsequence parameter descriptor */
+#define MS_INS_MASK 0x00ff /* mask to retrieve the instruction position < 256 XXX */
+#define MS_ARG_MASK 0x0f00 /* mask to retrieve the argument number */
+#define MS_TYP_MASK 0xf000 /* mask to retrieve the type of the param */
+
+/* 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_OP_ADELAY 13 /* adelay <val> */
+#define MS_OP_BRSTAT 14 /* brstat <mask>, <mask>, <offset> */
+#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_NULL 0
+#define MS_UNKNOWN MS_NULL
+
+/* predifined parameters */
+#define MS_ACCUM -1 /* use accum previously set by MS_OP_SET */
+
+/* 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_A 0x3
+#define MS_REG_EPP_D 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(accum) { MS_OP_SET, {{ (accum) }}}
+#define MS_BRSET(mask,offset) { MS_OP_BRSET, {{ (mask) }, { (offset) }}}
+#define MS_DBRA(offset) { MS_OP_DBRA, {{ (offset) }}}
+#define MS_BRCLEAR(mask,offset) { MS_OP_BRCLEAR, {{ (mask) }, { (offset) }}}
+#define MS_BRSTAT(mask_set,mask_clr,offset) \
+ { MS_OP_BRSTAT, {{ mask_set }, { mask_clr }, { (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(udelay) { MS_OP_DELAY, {{ (udelay) }}}
+
+/* asynchroneous delay in ms */
+#define MS_ADELAY(mdelay) { MS_OP_ADELAY, {{ (mdelay) }}}
+
+/* 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(bus,dev,body) ppb_MS_init(bus, dev, body, MS_OP_GET)
+
+#define ppb_MS_PUT_init(bus,dev,body) ppb_MS_init(bus, dev, body, MS_OP_PUT)
+
+extern int ppb_MS_init(
+ device_t, /* ppbus bus */
+ device_t, /* 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(
+ device_t, /* ppbus bus */
+ device_t, /* 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(
+ device_t, /* ppbus bus */
+ device_t, /* 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(
+ device_t, /* ppbus bus */
+ device_t, /* 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
new file mode 100644
index 0000000..ca6407f
--- /dev/null
+++ b/sys/dev/ppbus/ppbconf.c
@@ -0,0 +1,558 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#include "opt_ppb_1284.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_1284.h>
+
+#include "ppbus_if.h"
+
+#define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev))
+
+static MALLOC_DEFINE(M_PPBUSDEV, "ppbusdev", "Parallel Port bus device");
+
+
+/*
+ * Device methods
+ */
+
+static void
+ppbus_print_child(device_t bus, device_t dev)
+{
+ struct ppb_device *ppbdev;
+
+ bus_print_child_header(bus, dev);
+
+ ppbdev = (struct ppb_device *)device_get_ivars(dev);
+
+ if (ppbdev->flags != 0)
+ printf(" flags 0x%x", ppbdev->flags);
+
+ printf(" on %s%d\n", device_get_name(bus), device_get_unit(bus));
+
+ return;
+}
+
+static int
+ppbus_probe(device_t dev)
+{
+ device_set_desc(dev, "Parallel port bus");
+
+ return (0);
+}
+
+/*
+ * ppbus_add_child()
+ *
+ * Add a ppbus device, allocate/initialize the ivars
+ */
+static device_t
+ppbus_add_child(device_t dev, int order, const char *name, int unit)
+{
+ struct ppb_device *ppbdev;
+ device_t child;
+
+ /* allocate ivars for the new ppbus child */
+ ppbdev = malloc(sizeof(struct ppb_device), M_PPBUSDEV,
+ M_NOWAIT | M_ZERO);
+ if (!ppbdev)
+ return NULL;
+
+ /* initialize the ivars */
+ ppbdev->name = name;
+
+ /* add the device as a child to the ppbus bus with the allocated
+ * ivars */
+ child = device_add_child_ordered(dev, order, name, unit);
+ device_set_ivars(child, ppbdev);
+
+ return child;
+}
+
+static int
+ppbus_read_ivar(device_t bus, device_t dev, int index, uintptr_t* val)
+{
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
+
+ switch (index) {
+ case PPBUS_IVAR_MODE:
+ /* XXX yet device mode = ppbus mode = chipset mode */
+ *val = (u_long)ppb_get_mode(bus);
+ ppbdev->mode = (u_short)*val;
+ break;
+ case PPBUS_IVAR_AVM:
+ *val = (u_long)ppbdev->avm;
+ break;
+ case PPBUS_IVAR_IRQ:
+ BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_IRQ, val);
+ break;
+ default:
+ return (ENOENT);
+ }
+
+ return (0);
+}
+
+static int
+ppbus_write_ivar(device_t bus, device_t dev, int index, u_long val)
+{
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
+
+ switch (index) {
+ case PPBUS_IVAR_MODE:
+ /* XXX yet device mode = ppbus mode = chipset mode */
+ ppb_set_mode(bus,val);
+ ppbdev->mode = ppb_get_mode(bus);
+ break;
+ default:
+ return (ENOENT);
+ }
+
+ return (0);
+ }
+
+#define PPB_PNP_PRINTER 0
+#define PPB_PNP_MODEM 1
+#define PPB_PNP_NET 2
+#define PPB_PNP_HDC 3
+#define PPB_PNP_PCMCIA 4
+#define PPB_PNP_MEDIA 5
+#define PPB_PNP_FDC 6
+#define PPB_PNP_PORTS 7
+#define PPB_PNP_SCANNER 8
+#define PPB_PNP_DIGICAM 9
+
+#ifndef DONTPROBE_1284
+
+static char *pnp_tokens[] = {
+ "PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
+ "FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
+
+#if 0
+static char *pnp_classes[] = {
+ "printer", "modem", "network device",
+ "hard disk", "PCMCIA", "multimedia device",
+ "floppy disk", "ports", "scanner",
+ "digital camera", "unknown device", NULL };
+#endif
+
+/*
+ * search_token()
+ *
+ * Search the first occurence of a token within a string
+ */
+static char *
+search_token(char *str, int slen, char *token)
+{
+ int tlen, i;
+
+#define UNKNOWN_LENGTH -1
+
+ if (slen == UNKNOWN_LENGTH)
+ /* get string's length */
+ slen = strlen(str);
+
+ /* get token's length */
+ tlen = strlen(token);
+ if (tlen == 0)
+ return (str);
+
+ for (i = 0; i <= slen-tlen; i++) {
+ if (strncmp(str + i, token, tlen) == 0)
+ return (&str[i]);
+ }
+
+ return (NULL);
+}
+
+/*
+ * ppb_pnp_detect()
+ *
+ * Returns the class id. of the peripherial, -1 otherwise
+ */
+static int
+ppb_pnp_detect(device_t bus)
+{
+ char *token, *class = 0;
+ int i, len, error;
+ int class_id = -1;
+ char str[PPB_PnP_STRING_SIZE+1];
+ int unit = device_get_unit(bus);
+
+ printf("Probing for PnP devices on ppbus%d:\n", unit);
+
+ if ((error = ppb_1284_read_id(bus, PPB_NIBBLE, str,
+ PPB_PnP_STRING_SIZE, &len)))
+ goto end_detect;
+
+#ifdef DEBUG_1284
+ printf("ppb: <PnP> %d characters: ", len);
+ for (i = 0; i < len; i++)
+ printf("%c(0x%x) ", str[i], str[i]);
+ printf("\n");
+#endif
+
+ /* replace ';' characters by '\0' */
+ for (i = 0; i < len; i++)
+ str[i] = (str[i] == ';') ? '\0' : str[i];
+
+ if ((token = search_token(str, len, "MFG")) != NULL ||
+ (token = search_token(str, len, "MANUFACTURER")) != NULL)
+ printf("ppbus%d: <%s", unit,
+ search_token(token, UNKNOWN_LENGTH, ":") + 1);
+ else
+ printf("ppbus%d: <unknown", unit);
+
+ if ((token = search_token(str, len, "MDL")) != NULL ||
+ (token = search_token(str, len, "MODEL")) != NULL)
+ printf(" %s",
+ search_token(token, UNKNOWN_LENGTH, ":") + 1);
+ else
+ printf(" unknown");
+
+ if ((token = search_token(str, len, "VER")) != NULL)
+ printf("/%s",
+ search_token(token, UNKNOWN_LENGTH, ":") + 1);
+
+ if ((token = search_token(str, len, "REV")) != NULL)
+ printf(".%s",
+ search_token(token, UNKNOWN_LENGTH, ":") + 1);
+
+ printf(">");
+
+ if ((token = search_token(str, len, "CLS")) != NULL) {
+ class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
+ printf(" %s", class);
+ }
+
+ if ((token = search_token(str, len, "CMD")) != NULL ||
+ (token = search_token(str, len, "COMMAND")) != NULL)
+ printf(" %s",
+ search_token(token, UNKNOWN_LENGTH, ":") + 1);
+
+ printf("\n");
+
+ if (class)
+ /* identify class ident */
+ for (i = 0; pnp_tokens[i] != NULL; i++) {
+ if (search_token(class, len, pnp_tokens[i]) != NULL) {
+ class_id = i;
+ goto end_detect;
+ }
+ }
+
+ class_id = PPB_PnP_UNKNOWN;
+
+end_detect:
+ return (class_id);
+}
+
+/*
+ * ppb_scan_bus()
+ *
+ * Scan the ppbus for IEEE1284 compliant devices
+ */
+static int
+ppb_scan_bus(device_t bus)
+{
+ struct ppb_data * ppb = (struct ppb_data *)device_get_softc(bus);
+ int error = 0;
+ int unit = device_get_unit(bus);
+
+ /* try all IEEE1284 modes, for one device only
+ *
+ * XXX We should implement the IEEE1284.3 standard to detect
+ * daisy chained devices
+ */
+
+ error = ppb_1284_negociate(bus, PPB_NIBBLE, PPB_REQUEST_ID);
+
+ if ((ppb->state == PPB_ERROR) && (ppb->error == PPB_NOT_IEEE1284))
+ goto end_scan;
+
+ ppb_1284_terminate(bus);
+
+ printf("ppbus%d: IEEE1284 device found ", unit);
+
+ if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE, 0))) {
+ printf("/NIBBLE");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_PS2, 0))) {
+ printf("/PS2");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_ECP, 0))) {
+ printf("/ECP");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_ECP, PPB_USE_RLE))) {
+ printf("/ECP_RLE");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_EPP, 0))) {
+ printf("/EPP");
+ ppb_1284_terminate(bus);
+ }
+
+ /* try more IEEE1284 modes */
+ if (bootverbose) {
+ if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE,
+ PPB_REQUEST_ID))) {
+ printf("/NIBBLE_ID");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_PS2,
+ PPB_REQUEST_ID))) {
+ printf("/PS2_ID");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_ECP,
+ PPB_REQUEST_ID))) {
+ printf("/ECP_ID");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_ECP,
+ PPB_REQUEST_ID | PPB_USE_RLE))) {
+ printf("/ECP_RLE_ID");
+ ppb_1284_terminate(bus);
+ }
+
+ if (!(error = ppb_1284_negociate(bus, PPB_COMPATIBLE,
+ PPB_EXTENSIBILITY_LINK))) {
+ printf("/Extensibility Link");
+ ppb_1284_terminate(bus);
+ }
+ }
+
+ printf("\n");
+
+ /* detect PnP devices */
+ ppb->class_id = ppb_pnp_detect(bus);
+
+ return (0);
+
+end_scan:
+ return (error);
+}
+
+#endif /* !DONTPROBE_1284 */
+
+static int
+ppbus_attach(device_t dev)
+{
+
+ /* Locate our children */
+ bus_generic_probe(dev);
+
+#ifndef DONTPROBE_1284
+ /* detect IEEE1284 compliant devices */
+ ppb_scan_bus(dev);
+#endif /* !DONTPROBE_1284 */
+
+ /* launch attachement of the added children */
+ bus_generic_attach(dev);
+
+ return 0;
+}
+
+static int
+ppbus_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
+ void (*ihand)(void *), void *arg, void **cookiep)
+{
+ int error;
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(child);
+
+ /* a device driver must own the bus to register an interrupt */
+ if (ppb->ppb_owner != child)
+ return (EINVAL);
+
+ if ((error = BUS_SETUP_INTR(device_get_parent(bus), child, r, flags,
+ ihand, arg, cookiep)))
+ return (error);
+
+ /* store the resource and the cookie for eventually forcing
+ * handler unregistration
+ */
+ ppbdev->intr_cookie = *cookiep;
+ ppbdev->intr_resource = r;
+
+ return (0);
+}
+
+static int
+ppbus_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih)
+{
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(child);
+
+ /* a device driver must own the bus to unregister an interrupt */
+ if ((ppb->ppb_owner != child) || (ppbdev->intr_cookie != ih) ||
+ (ppbdev->intr_resource != r))
+ return (EINVAL);
+
+ ppbdev->intr_cookie = 0;
+ ppbdev->intr_resource = 0;
+
+ /* pass unregistration to the upper layer */
+ return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, ih));
+}
+
+/*
+ * ppb_request_bus()
+ *
+ * Allocate the device to perform transfers.
+ *
+ * how : PPB_WAIT or PPB_DONTWAIT
+ */
+int
+ppb_request_bus(device_t bus, device_t dev, int how)
+{
+ int s, error = 0;
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
+
+ while (!error) {
+ s = splhigh();
+ if (ppb->ppb_owner) {
+ splx(s);
+
+ switch (how) {
+ case (PPB_WAIT | PPB_INTR):
+ error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
+ break;
+
+ case (PPB_WAIT | PPB_NOINTR):
+ error = tsleep(ppb, PPBPRI, "ppbreq", 0);
+ break;
+
+ default:
+ return (EWOULDBLOCK);
+ break;
+ }
+
+ } 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 (ppbdev->ctx.valid)
+ ppb_set_mode(bus, ppbdev->ctx.mode);
+
+ splx(s);
+ return (0);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * ppb_release_bus()
+ *
+ * Release the device allocated with ppb_request_bus()
+ */
+int
+ppb_release_bus(device_t bus, device_t dev)
+{
+ int s, error;
+ struct ppb_data *ppb = DEVTOSOFTC(bus);
+ struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
+
+ if (ppbdev->intr_resource != 0)
+ /* force interrupt handler unregistration when the ppbus is released */
+ if ((error = BUS_TEARDOWN_INTR(bus, dev, ppbdev->intr_resource,
+ ppbdev->intr_cookie)))
+ return (error);
+
+ s = splhigh();
+ if (ppb->ppb_owner != dev) {
+ splx(s);
+ return (EACCES);
+ }
+
+ ppb->ppb_owner = 0;
+ splx(s);
+
+ /* save the context of the device */
+ ppbdev->ctx.mode = ppb_get_mode(bus);
+
+ /* ok, now the context of the device is valid */
+ ppbdev->ctx.valid = 1;
+
+ /* wakeup waiting processes */
+ wakeup(ppb);
+
+ return (0);
+}
+
+static devclass_t ppbus_devclass;
+
+static device_method_t ppbus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ppbus_probe),
+ DEVMETHOD(device_attach, ppbus_attach),
+
+ /* bus interface */
+ DEVMETHOD(bus_add_child, ppbus_add_child),
+ DEVMETHOD(bus_print_child, ppbus_print_child),
+ DEVMETHOD(bus_read_ivar, ppbus_read_ivar),
+ DEVMETHOD(bus_write_ivar, ppbus_write_ivar),
+ DEVMETHOD(bus_setup_intr, ppbus_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ppbus_teardown_intr),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+
+ { 0, 0 }
+};
+
+static driver_t ppbus_driver = {
+ "ppbus",
+ ppbus_methods,
+ sizeof(struct ppb_data),
+};
+DRIVER_MODULE(ppbus, ppc, ppbus_driver, ppbus_devclass, 0, 0);
diff --git a/sys/dev/ppbus/ppbconf.h b/sys/dev/ppbus/ppbconf.h
new file mode 100644
index 0000000..96b7b1b
--- /dev/null
+++ b/sys/dev/ppbus/ppbconf.h
@@ -0,0 +1,276 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#ifndef __PPBCONF_H
+#define __PPBCONF_H
+
+#include <sys/queue.h>
+
+/*
+ * Parallel Port Bus sleep/wakeup queue.
+ */
+#define PPBPRI (PZERO+8)
+
+/*
+ * Parallel Port Chipset mode masks.
+ * NIBBLE mode is supposed to be available under each other modes.
+ */
+#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 0x4 /* EPP mode, 32 bit */
+#define PPB_ECP 0x8 /* ECP mode */
+
+/* mode aliases */
+#define PPB_SPP PPB_NIBBLE|PPB_PS2
+#define PPB_BYTE PPB_PS2
+
+#define PPB_MASK 0x0f
+#define PPB_OPTIONS_MASK 0xf0
+
+#define PPB_IS_EPP(mode) (mode & PPB_EPP)
+#define PPB_IN_EPP_MODE(bus) (PPB_IS_EPP (ppb_get_mode (bus)))
+#define PPB_IN_NIBBLE_MODE(bus) (ppb_get_mode (bus) & PPB_NIBBLE)
+#define PPB_IN_PS2_MODE(bus) (ppb_get_mode (bus) & PPB_PS2)
+
+#define n(flags) (~(flags) & (flags))
+
+/*
+ * Parallel Port Chipset control bits.
+ */
+#define STROBE 0x01
+#define AUTOFEED 0x02
+#define nINIT 0x04
+#define SELECTIN 0x08
+#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.
+ */
+#define TIMEOUT 0x01
+#define nFAULT 0x08
+#define SELECT 0x10
+#define PERROR 0x20
+#define nACK 0x40
+#define nBUSY 0x80
+
+/*
+ * Structure to store status information.
+ */
+struct ppb_status {
+ unsigned char status;
+
+ unsigned int timeout:1;
+ unsigned int error:1;
+ unsigned int select:1;
+ unsigned int paper_end:1;
+ unsigned int ack:1;
+ unsigned int busy:1;
+};
+
+/* Parallel port bus I/O opcodes */
+#define PPB_OUTSB_EPP 1
+#define PPB_OUTSW_EPP 2
+#define PPB_OUTSL_EPP 3
+#define PPB_INSB_EPP 4
+#define PPB_INSW_EPP 5
+#define PPB_INSL_EPP 6
+#define PPB_RDTR 7
+#define PPB_RSTR 8
+#define PPB_RCTR 9
+#define PPB_REPP_A 10
+#define PPB_REPP_D 11
+#define PPB_RECR 12
+#define PPB_RFIFO 13
+#define PPB_WDTR 14
+#define PPB_WSTR 15
+#define PPB_WCTR 16
+#define PPB_WEPP_A 17
+#define PPB_WEPP_D 18
+#define PPB_WECR 19
+#define PPB_WFIFO 20
+
+/*
+ * How tsleep() is called in ppb_request_bus().
+ */
+#define PPB_DONTWAIT 0
+#define PPB_NOINTR 0
+#define PPB_WAIT 0x1
+#define PPB_INTR 0x2
+#define PPB_POLL 0x4
+#define PPB_FOREVER -1
+
+/*
+ * 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;
+ void *p;
+ char *c;
+ 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 */
+};
+
+/*
+ * List of IVARS available to ppb device drivers
+ */
+#define PPBUS_IVAR_MODE 0
+#define PPBUS_IVAR_AVM 1
+#define PPBUS_IVAR_IRQ 2
+
+/* other fields are reserved to the ppbus internals */
+
+struct ppb_device {
+
+ const char *name; /* name of the device */
+
+ ushort mode; /* current mode of the device */
+ ushort avm; /* available IEEE1284 modes of
+ * the device */
+ uint flags; /* flags */
+
+ 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];
+
+ struct resource *intr_resource;
+ void *intr_cookie;
+
+ void *drv1, *drv2; /* drivers private data */
+};
+
+/* EPP standards */
+#define EPP_1_9 0x0 /* default */
+#define EPP_1_7 0x1
+
+/* Parallel Port Chipset IVARS */ /* elsewhere XXX */
+#define PPC_IVAR_EPP_PROTO 0
+#define PPC_IVAR_IRQ 1
+
+/*
+ * Maximum size of the PnP info string
+ */
+#define PPB_PnP_STRING_SIZE 256 /* XXX */
+
+/*
+ * Parallel Port Bus structure.
+ */
+struct ppb_data {
+
+#define PPB_PnP_PRINTER 0
+#define PPB_PnP_MODEM 1
+#define PPB_PnP_NET 2
+#define PPB_PnP_HDC 3
+#define PPB_PnP_PCMCIA 4
+#define PPB_PnP_MEDIA 5
+#define PPB_PnP_FDC 6
+#define PPB_PnP_PORTS 7
+#define PPB_PnP_SCANNER 8
+#define PPB_PnP_DIGICAM 9
+#define PPB_PnP_UNKNOWN 10
+ int class_id; /* not a PnP device if class_id < 0 */
+
+ int state; /* current IEEE1284 state */
+ int error; /* last IEEE1284 error */
+
+ int mode; /* IEEE 1284-1994 mode
+ * NIBBLE, PS2, EPP or ECP */
+
+ void *ppb_owner; /* device which owns the bus */
+};
+
+#ifdef _KERNEL
+extern int ppb_attach_device(device_t);
+extern int ppb_request_bus(device_t, device_t, int);
+extern int ppb_release_bus(device_t, device_t);
+
+/* bus related functions */
+extern int ppb_get_status(device_t, struct ppb_status *);
+extern int ppb_poll_bus(device_t, int, char, char, int);
+extern int ppb_reset_epp_timeout(device_t);
+extern int ppb_ecp_sync(device_t);
+extern int ppb_get_epp_protocol(device_t);
+extern int ppb_set_mode(device_t, int); /* returns old mode */
+extern int ppb_get_mode(device_t); /* returns current mode */
+extern int ppb_write(device_t, char *, int, int);
+#endif /* _KERNEL */
+
+/*
+ * These are defined as macros for speedup.
+#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)
+ */
+
+#endif
diff --git a/sys/dev/ppbus/ppbio.h b/sys/dev/ppbus/ppbio.h
new file mode 100644
index 0000000..2794edd
--- /dev/null
+++ b/sys/dev/ppbus/ppbio.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1999 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef __PPBIO_H
+#define __PPBIO_H
+
+/*
+ * Set of ppbus i/o routines callable from ppbus device drivers
+ */
+
+#define ppb_outsb_epp(dev,buf,cnt) \
+ (PPBUS_IO(device_get_parent(dev), PPB_OUTSB_EPP, buf, cnt, 0))
+#define ppb_outsw_epp(dev,buf,cnt) \
+ (PPBUS_IO(device_get_parent(dev), PPB_OUTSW_EPP, buf, cnt, 0))
+#define ppb_outsl_epp(dev,buf,cnt) \
+ (PPBUS_IO(device_get_parent(dev), PPB_OUTSL_EPP, buf, cnt, 0))
+
+#define ppb_insb_epp(dev,buf,cnt) \
+ (PPBUS_IO(device_get_parent(dev), PPB_INSB_EPP, buf, cnt, 0))
+#define ppb_insw_epp(dev,buf,cnt) \
+ (PPBUS_IO(device_get_parent(dev), PPB_INSW_EPP, buf, cnt, 0))
+#define ppb_insl_epp(dev,buf,cnt) \
+ (PPBUS_IO(device_get_parent(dev), PPB_INSL_EPP, buf, cnt, 0))
+
+#define ppb_repp_A(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_REPP_A, 0, 0, 0))
+#define ppb_repp_D(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_REPP_D, 0, 0, 0))
+#define ppb_recr(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_RECR, 0, 0, 0))
+#define ppb_rfifo(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_RFIFO, 0, 0, 0))
+
+#define ppb_wepp_A(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WEPP_A, 0, 0, byte))
+#define ppb_wepp_D(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WEPP_D, 0, 0, byte))
+#define ppb_wecr(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WECR, 0, 0, byte))
+#define ppb_wfifo(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WFIFO, 0, 0, byte))
+
+#define ppb_rdtr(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_RDTR, 0, 0, 0))
+#define ppb_rstr(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_RSTR, 0, 0, 0))
+#define ppb_rctr(dev) \
+ (PPBUS_IO(device_get_parent(dev), PPB_RCTR, 0, 0, 0))
+
+#define ppb_wdtr(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WDTR, 0, 0, byte))
+#define ppb_wstr(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WSTR, 0, 0, byte))
+#define ppb_wctr(dev,byte) \
+ (PPBUS_IO(device_get_parent(dev), PPB_WCTR, 0, 0, byte))
+
+#endif
diff --git a/sys/dev/ppbus/ppbus_if.m b/sys/dev/ppbus/ppbus_if.m
new file mode 100644
index 0000000..f21dd83
--- /dev/null
+++ b/sys/dev/ppbus/ppbus_if.m
@@ -0,0 +1,93 @@
+#
+# Copyright (c) 1999 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.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+#include <dev/ppbus/ppbconf.h>
+
+INTERFACE ppbus;
+
+#
+# Do low level i/o operations
+#
+METHOD u_char io {
+ device_t dev;
+ int opcode;
+ u_char *addr;
+ int cnt;
+ u_char byte;
+};
+
+#
+# Execution of a microsequence
+#
+METHOD int exec_microseq {
+ device_t dev;
+ struct ppb_microseq **ppb_microseq;
+};
+
+#
+# Reset EPP timeout
+#
+METHOD int reset_epp {
+ device_t dev;
+}
+
+#
+# Set chipset mode
+#
+METHOD int setmode {
+ device_t dev;
+ int mode;
+}
+
+#
+# Synchronize ECP FIFO
+#
+METHOD int ecp_sync {
+ device_t dev;
+}
+
+#
+# Do chipset dependent low level read
+#
+METHOD int read {
+ device_t dev;
+ char *buf;
+ int len;
+ int how;
+}
+
+#
+# Do chipset dependent low level write
+#
+METHOD int write {
+ device_t dev;
+ char *buf;
+ int len;
+ int how;
+}
diff --git a/sys/dev/ppbus/ppi.c b/sys/dev/ppbus/ppi.c
new file mode 100644
index 0000000..d2cf4fd
--- /dev/null
+++ b/sys/dev/ppbus/ppi.c
@@ -0,0 +1,578 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999 Nicolas Souchu, Michael Smith
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#include "opt_ppb_1284.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+
+#ifdef PERIPH_1284
+#include <dev/ppbus/ppb_1284.h>
+#endif
+
+#include <dev/ppbus/ppi.h>
+
+#include "ppbus_if.h"
+
+#include <dev/ppbus/ppbio.h>
+
+#define BUFSIZE 512
+
+struct ppi_data {
+
+ int ppi_unit;
+ int ppi_flags;
+#define HAVE_PPBUS (1<<0)
+#define HAD_PPBUS (1<<1)
+
+ int ppi_count;
+ int ppi_mode; /* IEEE1284 mode */
+ char ppi_buffer[BUFSIZE];
+
+#ifdef PERIPH_1284
+ struct resource *intr_resource; /* interrupt resource */
+ void *intr_cookie; /* interrupt registration cookie */
+#endif /* PERIPH_1284 */
+};
+
+#define DEVTOSOFTC(dev) \
+ ((struct ppi_data *)device_get_softc(dev))
+#define UNITOSOFTC(unit) \
+ ((struct ppi_data *)devclass_get_softc(ppi_devclass, (unit)))
+#define UNITODEVICE(unit) \
+ (devclass_get_device(ppi_devclass, (unit)))
+
+static devclass_t ppi_devclass;
+
+static d_open_t ppiopen;
+static d_close_t ppiclose;
+static d_ioctl_t ppiioctl;
+static d_write_t ppiwrite;
+static d_read_t ppiread;
+
+#define CDEV_MAJOR 82
+static struct cdevsw ppi_cdevsw = {
+ /* open */ ppiopen,
+ /* close */ ppiclose,
+ /* read */ ppiread,
+ /* write */ ppiwrite,
+ /* ioctl */ ppiioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "ppi",
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+#ifdef PERIPH_1284
+
+static void
+ppi_enable_intr(device_t ppidev)
+{
+ char r;
+ device_t ppbus = device_get_parent(ppidev);
+
+ r = ppb_rctr(ppbus);
+ ppb_wctr(ppbus, r | IRQENABLE);
+
+ return;
+}
+
+static void
+ppi_disable_intr(device_t ppidev)
+{
+ char r;
+ device_t ppbus = device_get_parent(ppidev);
+
+ r = ppb_rctr(ppbus);
+ ppb_wctr(ppbus, r & ~IRQENABLE);
+
+ return;
+}
+
+#endif /* PERIPH_1284 */
+
+static void
+ppi_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, "ppi", -1);
+}
+
+/*
+ * ppi_probe()
+ */
+static int
+ppi_probe(device_t dev)
+{
+ struct ppi_data *ppi;
+
+ /* probe is always ok */
+ device_set_desc(dev, "Parallel I/O");
+
+ ppi = DEVTOSOFTC(dev);
+ bzero(ppi, sizeof(struct ppi_data));
+
+ return (0);
+}
+
+/*
+ * ppi_attach()
+ */
+static int
+ppi_attach(device_t dev)
+{
+#ifdef PERIPH_1284
+ uintptr_t irq;
+ int zero = 0;
+ struct ppi_data *ppi = DEVTOSOFTC(dev);
+
+ /* retrive the irq */
+ BUS_READ_IVAR(device_get_parent(dev), dev, PPBUS_IVAR_IRQ, &irq);
+
+ /* declare our interrupt handler */
+ ppi->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &zero, irq, irq, 1, RF_ACTIVE);
+#endif /* PERIPH_1284 */
+
+ make_dev(&ppi_cdevsw, device_get_unit(dev), /* XXX cleanup */
+ UID_ROOT, GID_WHEEL,
+ 0600, "ppi%d", device_get_unit(dev));
+
+ return (0);
+}
+
+#ifdef PERIPH_1284
+/*
+ * Cable
+ * -----
+ *
+ * Use an IEEE1284 compliant (DB25/DB25) cable with the following tricks:
+ *
+ * nStrobe <-> nAck 1 <-> 10
+ * nAutofd <-> Busy 11 <-> 14
+ * nSelectin <-> Select 17 <-> 13
+ * nInit <-> nFault 15 <-> 16
+ *
+ */
+static void
+ppiintr(void *arg)
+{
+ device_t ppidev = (device_t)arg;
+ device_t ppbus = device_get_parent(ppidev);
+ struct ppi_data *ppi = DEVTOSOFTC(ppidev);
+
+ ppi_disable_intr(ppidev);
+
+ switch (ppb_1284_get_state(ppbus)) {
+
+ /* accept IEEE1284 negociation then wakeup an waiting process to
+ * continue negociation at process level */
+ case PPB_FORWARD_IDLE:
+ /* Event 1 */
+ if ((ppb_rstr(ppbus) & (SELECT | nBUSY)) ==
+ (SELECT | nBUSY)) {
+ /* IEEE1284 negociation */
+#ifdef DEBUG_1284
+ printf("N");
+#endif
+
+ /* Event 2 - prepare for reading the ext. value */
+ ppb_wctr(ppbus, (PCD | STROBE | nINIT) & ~SELECTIN);
+
+ ppb_1284_set_state(ppbus, PPB_NEGOCIATION);
+
+ } else {
+#ifdef DEBUG_1284
+ printf("0x%x", ppb_rstr(ppbus));
+#endif
+ ppb_peripheral_terminate(ppbus, PPB_DONTWAIT);
+ break;
+ }
+
+ /* wake up any process waiting for negociation from
+ * remote master host */
+
+ /* XXX should set a variable to warn the process about
+ * the interrupt */
+
+ wakeup(ppi);
+ break;
+ default:
+#ifdef DEBUG_1284
+ printf("?%d", ppb_1284_get_state(ppbus));
+#endif
+ ppb_1284_set_state(ppbus, PPB_FORWARD_IDLE);
+ ppb_set_mode(ppbus, PPB_COMPATIBLE);
+ break;
+ }
+
+ ppi_enable_intr(ppidev);
+
+ return;
+}
+#endif /* PERIPH_1284 */
+
+static int
+ppiopen(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ u_int unit = minor(dev);
+ struct ppi_data *ppi = UNITOSOFTC(unit);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ int res;
+
+ if (!ppi)
+ return (ENXIO);
+
+ if (!(ppi->ppi_flags & HAVE_PPBUS)) {
+ if ((res = ppb_request_bus(ppbus, ppidev,
+ (flags & O_NONBLOCK) ? PPB_DONTWAIT :
+ (PPB_WAIT | PPB_INTR))))
+ return (res);
+
+ ppi->ppi_flags |= HAVE_PPBUS;
+
+#ifdef PERIPH_1284
+ if (ppi->intr_resource) {
+ /* register our interrupt handler */
+ BUS_SETUP_INTR(device_get_parent(ppidev), ppidev, ppi->intr_resource,
+ INTR_TYPE_TTY, ppiintr, dev, &ppi->intr_cookie);
+ }
+#endif /* PERIPH_1284 */
+ }
+ ppi->ppi_count += 1;
+
+ return (0);
+}
+
+static int
+ppiclose(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ u_int unit = minor(dev);
+ struct ppi_data *ppi = UNITOSOFTC(unit);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+
+ ppi->ppi_count --;
+ if (!ppi->ppi_count) {
+
+#ifdef PERIPH_1284
+ switch (ppb_1284_get_state(ppbus)) {
+ case PPB_PERIPHERAL_IDLE:
+ ppb_peripheral_terminate(ppbus, 0);
+ break;
+ case PPB_REVERSE_IDLE:
+ case PPB_EPP_IDLE:
+ case PPB_ECP_FORWARD_IDLE:
+ default:
+ ppb_1284_terminate(ppbus);
+ break;
+ }
+#endif /* PERIPH_1284 */
+
+ /* unregistration of interrupt forced by release */
+ ppb_release_bus(ppbus, ppidev);
+
+ ppi->ppi_flags &= ~HAVE_PPBUS;
+ }
+
+ return (0);
+}
+
+/*
+ * ppiread()
+ *
+ * IEEE1284 compliant read.
+ *
+ * First, try negociation to BYTE then NIBBLE mode
+ * If no data is available, wait for it otherwise transfer as much as possible
+ */
+static int
+ppiread(dev_t dev, struct uio *uio, int ioflag)
+{
+#ifdef PERIPH_1284
+ u_int unit = minor(dev);
+ struct ppi_data *ppi = UNITOSOFTC(unit);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ int len, error = 0;
+
+ switch (ppb_1284_get_state(ppbus)) {
+ case PPB_PERIPHERAL_IDLE:
+ ppb_peripheral_terminate(ppbus, 0);
+ /* FALLTHROUGH */
+
+ case PPB_FORWARD_IDLE:
+ /* if can't negociate NIBBLE mode then try BYTE mode,
+ * the peripheral may be a computer
+ */
+ if ((ppb_1284_negociate(ppbus,
+ ppi->ppi_mode = PPB_NIBBLE, 0))) {
+
+ /* XXX Wait 2 seconds to let the remote host some
+ * time to terminate its interrupt
+ */
+ tsleep(ppi, PPBPRI, "ppiread", 2*hz);
+
+ if ((error = ppb_1284_negociate(ppbus,
+ ppi->ppi_mode = PPB_BYTE, 0)))
+ return (error);
+ }
+ break;
+
+ case PPB_REVERSE_IDLE:
+ case PPB_EPP_IDLE:
+ case PPB_ECP_FORWARD_IDLE:
+ default:
+ break;
+ }
+
+#ifdef DEBUG_1284
+ printf("N");
+#endif
+ /* read data */
+ len = 0;
+ while (uio->uio_resid) {
+ if ((error = ppb_1284_read(ppbus, ppi->ppi_mode,
+ ppi->ppi_buffer, min(BUFSIZE, uio->uio_resid),
+ &len))) {
+ goto error;
+ }
+
+ if (!len)
+ goto error; /* no more data */
+
+#ifdef DEBUG_1284
+ printf("d");
+#endif
+ if ((error = uiomove(ppi->ppi_buffer, len, uio)))
+ goto error;
+ }
+
+error:
+
+#else /* PERIPH_1284 */
+ int error = ENODEV;
+#endif
+
+ return (error);
+}
+
+/*
+ * ppiwrite()
+ *
+ * IEEE1284 compliant write
+ *
+ * Actually, this is the peripheral side of a remote IEEE1284 read
+ *
+ * The first part of the negociation (IEEE1284 device detection) is
+ * done at interrupt level, then the remaining is done by the writing
+ * process
+ *
+ * Once negociation done, transfer data
+ */
+static int
+ppiwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+#ifdef PERIPH_1284
+ u_int unit = minor(dev);
+ struct ppi_data *ppi = UNITOSOFTC(unit);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ int len, error = 0, sent;
+
+#if 0
+ int ret;
+
+ #define ADDRESS MS_PARAM(0, 0, MS_TYP_PTR)
+ #define LENGTH MS_PARAM(0, 1, MS_TYP_INT)
+
+ struct ppb_microseq msq[] = {
+ { MS_OP_PUT, { MS_UNKNOWN, MS_UNKNOWN, MS_UNKNOWN } },
+ MS_RET(0)
+ };
+
+ /* negociate ECP mode */
+ if (ppb_1284_negociate(ppbus, PPB_ECP, 0)) {
+ printf("ppiwrite: ECP negociation failed\n");
+ }
+
+ while (!error && (len = min(uio->uio_resid, BUFSIZE))) {
+ uiomove(ppi->ppi_buffer, len, uio);
+
+ ppb_MS_init_msq(msq, 2, ADDRESS, ppi->ppi_buffer, LENGTH, len);
+
+ error = ppb_MS_microseq(ppbus, msq, &ret);
+ }
+#endif
+
+ /* we have to be peripheral to be able to send data, so
+ * wait for the appropriate state
+ */
+ if (ppb_1284_get_state(ppbus) < PPB_PERIPHERAL_NEGOCIATION)
+ ppb_1284_terminate(ppbus);
+
+ while (ppb_1284_get_state(ppbus) != PPB_PERIPHERAL_IDLE) {
+ /* XXX should check a variable before sleeping */
+#ifdef DEBUG_1284
+ printf("s");
+#endif
+
+ ppi_enable_intr(ppidev);
+
+ /* sleep until IEEE1284 negociation starts */
+ error = tsleep(ppi, PCATCH | PPBPRI, "ppiwrite", 0);
+
+ switch (error) {
+ case 0:
+ /* negociate peripheral side with BYTE mode */
+ ppb_peripheral_negociate(ppbus, PPB_BYTE, 0);
+ break;
+ case EWOULDBLOCK:
+ break;
+ default:
+ goto error;
+ }
+ }
+#ifdef DEBUG_1284
+ printf("N");
+#endif
+
+ /* negociation done, write bytes to master host */
+ while ((len = min(uio->uio_resid, BUFSIZE)) != 0) {
+ uiomove(ppi->ppi_buffer, len, uio);
+ if ((error = byte_peripheral_write(ppbus,
+ ppi->ppi_buffer, len, &sent)))
+ goto error;
+#ifdef DEBUG_1284
+ printf("d");
+#endif
+ }
+
+error:
+
+#else /* PERIPH_1284 */
+ int error = ENODEV;
+#endif
+
+ return (error);
+}
+
+static int
+ppiioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
+{
+ u_int unit = minor(dev);
+ device_t ppidev = UNITODEVICE(unit);
+ device_t ppbus = device_get_parent(ppidev);
+ int error = 0;
+ u_int8_t *val = (u_int8_t *)data;
+
+ switch (cmd) {
+
+ case PPIGDATA: /* get data register */
+ *val = ppb_rdtr(ppbus);
+ break;
+ case PPIGSTATUS: /* get status bits */
+ *val = ppb_rstr(ppbus);
+ break;
+ case PPIGCTRL: /* get control bits */
+ *val = ppb_rctr(ppbus);
+ break;
+ case PPIGEPPD: /* get EPP data bits */
+ *val = ppb_repp_D(ppbus);
+ break;
+ case PPIGECR: /* get ECP bits */
+ *val = ppb_recr(ppbus);
+ break;
+ case PPIGFIFO: /* read FIFO */
+ *val = ppb_rfifo(ppbus);
+ break;
+ case PPISDATA: /* set data register */
+ ppb_wdtr(ppbus, *val);
+ break;
+ case PPISSTATUS: /* set status bits */
+ ppb_wstr(ppbus, *val);
+ break;
+ case PPISCTRL: /* set control bits */
+ ppb_wctr(ppbus, *val);
+ break;
+ case PPISEPPD: /* set EPP data bits */
+ ppb_wepp_D(ppbus, *val);
+ break;
+ case PPISECR: /* set ECP bits */
+ ppb_wecr(ppbus, *val);
+ break;
+ case PPISFIFO: /* write FIFO */
+ ppb_wfifo(ppbus, *val);
+ break;
+ case PPIGEPPA: /* get EPP address bits */
+ *val = ppb_repp_A(ppbus);
+ break;
+ case PPISEPPA: /* set EPP address bits */
+ ppb_wepp_A(ppbus, *val);
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+
+static device_method_t ppi_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, ppi_identify),
+ DEVMETHOD(device_probe, ppi_probe),
+ DEVMETHOD(device_attach, ppi_attach),
+
+ { 0, 0 }
+};
+
+static driver_t ppi_driver = {
+ "ppi",
+ ppi_methods,
+ sizeof(struct ppi_data),
+};
+DRIVER_MODULE(ppi, ppbus, ppi_driver, ppi_devclass, 0, 0);
diff --git a/sys/dev/ppbus/ppi.h b/sys/dev/ppbus/ppi.h
new file mode 100644
index 0000000..8ff2fb4
--- /dev/null
+++ b/sys/dev/ppbus/ppi.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1997 Nicolas Souchu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+#ifndef __PPI_H
+#define __PPI_H
+
+#ifndef _KERNEL
+# include <sys/types.h>
+#endif
+#include <sys/ioccom.h>
+
+#define PPIGDATA _IOR('P', 10, u_int8_t)
+#define PPIGSTATUS _IOR('P', 11, u_int8_t)
+#define PPIGCTRL _IOR('P', 12, u_int8_t)
+#define PPIGEPPD _IOR('P', 13, u_int8_t)
+#define PPIGECR _IOR('P', 14, u_int8_t)
+#define PPIGFIFO _IOR('P', 15, u_int8_t)
+
+#define PPISDATA _IOW('P', 16, u_int8_t)
+#define PPISSTATUS _IOW('P', 17, u_int8_t)
+#define PPISCTRL _IOW('P', 18, u_int8_t)
+#define PPISEPPD _IOW('P', 19, u_int8_t)
+#define PPISECR _IOW('P', 20, u_int8_t)
+#define PPISFIFO _IOW('P', 21, u_int8_t)
+
+#define PPIGEPPA _IOR('P', 22, u_int8_t)
+#define PPISEPPA _IOR('P', 23, u_int8_t)
+
+#endif
diff --git a/sys/dev/ppbus/pps.c b/sys/dev/ppbus/pps.c
new file mode 100644
index 0000000..b201757
--- /dev/null
+++ b/sys/dev/ppbus/pps.c
@@ -0,0 +1,320 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * This driver implements a draft-mogul-pps-api-02.txt PPS source.
+ *
+ * The input pin is pin#10
+ * The echo output pin is pin#14
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/timepps.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/ppbus/ppbconf.h>
+#include "ppbus_if.h"
+#include <dev/ppbus/ppbio.h>
+
+#define PPS_NAME "pps" /* our official name */
+
+#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg);
+
+struct pps_data {
+ struct ppb_device pps_dev;
+ struct pps_state pps[9];
+ dev_t devs[9];
+ device_t ppsdev;
+ device_t ppbus;
+ int busy;
+ struct callout_handle timeout;
+ int lastdata;
+
+ struct resource *intr_resource; /* interrupt resource */
+ void *intr_cookie; /* interrupt registration cookie */
+};
+
+static void ppsintr(void *arg);
+static void ppshcpoll(void *arg);
+
+#define DEVTOSOFTC(dev) \
+ ((struct pps_data *)device_get_softc(dev))
+
+static devclass_t pps_devclass;
+
+static d_open_t ppsopen;
+static d_close_t ppsclose;
+static d_ioctl_t ppsioctl;
+
+#define CDEV_MAJOR 89
+static struct cdevsw pps_cdevsw = {
+ /* open */ ppsopen,
+ /* close */ ppsclose,
+ /* read */ noread,
+ /* write */ nowrite,
+ /* ioctl */ ppsioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ PPS_NAME,
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+};
+
+static void
+ppsidentify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, PPS_NAME, -1);
+}
+
+static int
+ppstry(device_t ppbus, int send, int expect)
+{
+ int i;
+
+ ppb_wdtr(ppbus, send);
+ i = ppb_rdtr(ppbus);
+ PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i);
+ return (i != expect);
+}
+
+static int
+ppsprobe(device_t ppsdev)
+{
+ device_set_desc(ppsdev, "Pulse per second Timing Interface");
+
+ return (0);
+}
+
+static int
+ppsattach(device_t dev)
+{
+ struct pps_data *sc = DEVTOSOFTC(dev);
+ device_t ppbus = device_get_parent(dev);
+ dev_t d;
+ intptr_t irq;
+ int i, unit, zero = 0;
+
+ bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */
+
+ /* retrieve the ppbus irq */
+ BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
+
+ if (irq > 0) {
+ /* declare our interrupt handler */
+ sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &zero, irq, irq, 1, RF_SHAREABLE);
+ }
+ /* interrupts seem mandatory */
+ if (sc->intr_resource == NULL)
+ return (ENXIO);
+
+ sc->ppsdev = dev;
+ sc->ppbus = ppbus;
+ unit = device_get_unit(ppbus);
+ d = make_dev(&pps_cdevsw, unit,
+ UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", unit);
+ sc->devs[0] = d;
+ sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
+ d->si_drv1 = sc;
+ d->si_drv2 = (void*)0;
+ pps_init(&sc->pps[0]);
+
+ if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT))
+ return (0);
+
+ do {
+ i = ppb_set_mode(sc->ppbus, PPB_EPP);
+ PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
+ if (i == -1)
+ break;
+ i = 0;
+ ppb_wctr(ppbus, i);
+ if (ppstry(ppbus, 0x00, 0x00))
+ break;
+ if (ppstry(ppbus, 0x55, 0x55))
+ break;
+ if (ppstry(ppbus, 0xaa, 0xaa))
+ break;
+ if (ppstry(ppbus, 0xff, 0xff))
+ break;
+
+ i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN;
+ ppb_wctr(ppbus, i);
+ PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
+ if (ppstry(ppbus, 0x00, 0x00))
+ break;
+ if (ppstry(ppbus, 0x55, 0x00))
+ break;
+ if (ppstry(ppbus, 0xaa, 0x00))
+ break;
+ if (ppstry(ppbus, 0xff, 0x00))
+ break;
+
+ i = IRQENABLE | PCD | nINIT | SELECTIN;
+ ppb_wctr(ppbus, i);
+ PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
+ ppstry(ppbus, 0x00, 0xff);
+ ppstry(ppbus, 0x55, 0xff);
+ ppstry(ppbus, 0xaa, 0xff);
+ ppstry(ppbus, 0xff, 0xff);
+
+ for (i = 1; i < 9; i++) {
+ d = make_dev(&pps_cdevsw, unit + 0x10000 * i,
+ UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%db%d", unit, i - 1);
+ sc->devs[i] = d;
+ sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
+ d->si_drv1 = sc;
+ d->si_drv2 = (void *)(intptr_t)i;
+ pps_init(&sc->pps[i]);
+ }
+ } while (0);
+ i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE);
+ ppb_release_bus(ppbus, dev);
+
+ return (0);
+}
+
+static int
+ppsopen(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ struct pps_data *sc = dev->si_drv1;
+ int subdev = (intptr_t)dev->si_drv2;
+ int error, i;
+
+ if (!sc->busy) {
+ device_t ppsdev = sc->ppsdev;
+ device_t ppbus = sc->ppbus;
+
+ if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR))
+ return (EINTR);
+
+ /* attach the interrupt handler */
+ if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource,
+ INTR_TYPE_TTY, ppsintr, ppsdev,
+ &sc->intr_cookie))) {
+ ppb_release_bus(ppbus, ppsdev);
+ return (error);
+ }
+
+ i = ppb_set_mode(sc->ppbus, PPB_PS2);
+ PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
+
+ i = IRQENABLE | PCD | nINIT | SELECTIN;
+ ppb_wctr(ppbus, i);
+ }
+ if (subdev > 0 && !(sc->busy & ~1)) {
+ sc->timeout = timeout(ppshcpoll, sc, 1);
+ sc->lastdata = ppb_rdtr(sc->ppbus);
+ }
+ sc->busy |= (1 << subdev);
+ return(0);
+}
+
+static int
+ppsclose(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ struct pps_data *sc = dev->si_drv1;
+ int subdev = (intptr_t)dev->si_drv2;
+
+ sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */
+ sc->busy &= ~(1 << subdev);
+ if (subdev > 0 && !(sc->busy & ~1))
+ untimeout(ppshcpoll, sc, sc->timeout);
+ if (!sc->busy) {
+ device_t ppsdev = sc->ppsdev;
+ device_t ppbus = sc->ppbus;
+
+ ppb_wdtr(ppbus, 0);
+ ppb_wctr(ppbus, 0);
+
+ /* Note: the interrupt handler is automatically detached */
+ ppb_set_mode(ppbus, PPB_COMPATIBLE);
+ ppb_release_bus(ppbus, ppsdev);
+ }
+ return(0);
+}
+
+static void
+ppshcpoll(void *arg)
+{
+ struct pps_data *sc = arg;
+ int i, j, k, l;
+
+ if (!(sc->busy & ~1))
+ return;
+ sc->timeout = timeout(ppshcpoll, sc, 1);
+ i = ppb_rdtr(sc->ppbus);
+ if (i == sc->lastdata)
+ return;
+ l = sc->lastdata ^ i;
+ k = 1;
+ for (j = 1; j < 9; j ++) {
+ if (l & k) {
+ pps_capture(&sc->pps[j]);
+ pps_event(&sc->pps[j],
+ i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
+ }
+ k += k;
+ }
+ sc->lastdata = i;
+}
+
+static void
+ppsintr(void *arg)
+{
+ device_t ppsdev = (device_t)arg;
+ struct pps_data *sc = DEVTOSOFTC(ppsdev);
+ device_t ppbus = sc->ppbus;
+
+ pps_capture(&sc->pps[0]);
+ if (!(ppb_rstr(ppbus) & nACK))
+ return;
+ if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
+ ppb_wctr(ppbus, IRQENABLE | AUTOFEED);
+ pps_event(&sc->pps[0], PPS_CAPTUREASSERT);
+ if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
+ ppb_wctr(ppbus, IRQENABLE);
+}
+
+static int
+ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
+{
+ struct pps_data *sc = dev->si_drv1;
+ int subdev = (intptr_t)dev->si_drv2;
+
+ return (pps_ioctl(cmd, data, &sc->pps[subdev]));
+}
+
+static device_method_t pps_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, ppsidentify),
+ DEVMETHOD(device_probe, ppsprobe),
+ DEVMETHOD(device_attach, ppsattach),
+
+ { 0, 0 }
+};
+
+static driver_t pps_driver = {
+ PPS_NAME,
+ pps_methods,
+ sizeof(struct pps_data),
+};
+DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0);
diff --git a/sys/dev/ppbus/vpo.c b/sys/dev/ppbus/vpo.c
new file mode 100644
index 0000000..ad83e2f
--- /dev/null
+++ b/sys/dev/ppbus/vpo.c
@@ -0,0 +1,463 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/devicestat.h> /* for struct devstat */
+
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_periph.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_da.h>
+
+#include <sys/kernel.h>
+
+#include "opt_vpo.h"
+
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/vpoio.h>
+
+#include "ppbus_if.h"
+
+struct vpo_sense {
+ struct scsi_sense cmd;
+ unsigned int stat;
+ unsigned int count;
+};
+
+struct vpo_data {
+ unsigned short vpo_unit;
+
+ int vpo_stat;
+ int vpo_count;
+ int vpo_error;
+
+ int vpo_isplus;
+
+ struct cam_sim *sim;
+
+ struct vpo_sense vpo_sense;
+
+ struct vpoio_data vpo_io; /* interface to low level functions */
+};
+
+#define DEVTOSOFTC(dev) \
+ ((struct vpo_data *)device_get_softc(dev))
+
+/* cam related functions */
+static void vpo_action(struct cam_sim *sim, union ccb *ccb);
+static void vpo_poll(struct cam_sim *sim);
+static void vpo_cam_rescan_callback(struct cam_periph *periph,
+ union ccb *ccb);
+static void vpo_cam_rescan(struct vpo_data *vpo);
+
+static void
+vpo_identify(driver_t *driver, device_t parent)
+{
+
+ BUS_ADD_CHILD(parent, 0, "vpo", -1);
+}
+
+/*
+ * vpo_probe()
+ */
+static int
+vpo_probe(device_t dev)
+{
+ struct vpo_data *vpo;
+ int error;
+
+ vpo = DEVTOSOFTC(dev);
+ bzero(vpo, sizeof(struct vpo_data));
+
+ /* vpo dependent initialisation */
+ vpo->vpo_unit = device_get_unit(dev);
+
+ /* low level probe */
+ vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit);
+
+ /* check ZIP before ZIP+ or imm_probe() will send controls to
+ * the printer or whatelse connected to the port */
+ if ((error = vpoio_probe(dev, &vpo->vpo_io)) == 0) {
+ vpo->vpo_isplus = 0;
+ device_set_desc(dev,
+ "Iomega VPI0 Parallel to SCSI interface");
+ } else if ((error = imm_probe(dev, &vpo->vpo_io)) == 0) {
+ vpo->vpo_isplus = 1;
+ device_set_desc(dev,
+ "Iomega Matchmaker Parallel to SCSI interface");
+ } else {
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * vpo_attach()
+ */
+static int
+vpo_attach(device_t dev)
+{
+ struct vpo_data *vpo = DEVTOSOFTC(dev);
+ struct cam_devq *devq;
+ int error;
+
+ /* low level attachment */
+ if (vpo->vpo_isplus) {
+ if ((error = imm_attach(&vpo->vpo_io)))
+ return (error);
+ } else {
+ if ((error = vpoio_attach(&vpo->vpo_io)))
+ return (error);
+ }
+
+ /*
+ ** Now tell the generic SCSI layer
+ ** about our bus.
+ */
+ devq = cam_simq_alloc(/*maxopenings*/1);
+ /* XXX What about low-level detach on error? */
+ if (devq == NULL)
+ return (ENXIO);
+
+ vpo->sim = cam_sim_alloc(vpo_action, vpo_poll, "vpo", vpo,
+ device_get_unit(dev),
+ /*untagged*/1, /*tagged*/0, devq);
+ if (vpo->sim == NULL) {
+ cam_simq_free(devq);
+ return (ENXIO);
+ }
+
+ if (xpt_bus_register(vpo->sim, /*bus*/0) != CAM_SUCCESS) {
+ cam_sim_free(vpo->sim, /*free_devq*/TRUE);
+ return (ENXIO);
+ }
+
+ /* all went ok */
+
+ vpo_cam_rescan(vpo); /* have CAM rescan the bus */
+
+ return (0);
+}
+
+static void
+vpo_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+ free(ccb, M_TEMP);
+}
+
+static void
+vpo_cam_rescan(struct vpo_data *vpo)
+{
+ struct cam_path *path;
+ union ccb *ccb = malloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO);
+
+ if (xpt_create_path(&path, xpt_periph, cam_sim_path(vpo->sim), 0, 0)
+ != CAM_REQ_CMP) {
+ /* A failure is benign as the user can do a manual rescan */
+ return;
+ }
+
+ xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/);
+ ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ ccb->ccb_h.cbfcnp = vpo_cam_rescan_callback;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_action(ccb);
+
+ /* The scan is in progress now. */
+}
+
+/*
+ * vpo_intr()
+ */
+static void
+vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio)
+{
+ int errno; /* error in errno.h */
+ int s;
+#ifdef VP0_DEBUG
+ int i;
+#endif
+
+ s = splcam();
+
+ if (vpo->vpo_isplus) {
+ errno = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
+ csio->ccb_h.target_id,
+ (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len,
+ (char *)csio->data_ptr, csio->dxfer_len,
+ &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error);
+ } else {
+ errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
+ csio->ccb_h.target_id,
+ (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len,
+ (char *)csio->data_ptr, csio->dxfer_len,
+ &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",
+ errno, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error);
+
+ /* dump of command */
+ for (i=0; i<csio->cdb_len; i++)
+ printf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]);
+
+ printf("\n");
+#endif
+
+ if (errno) {
+ /* connection to ppbus interrupted */
+ csio->ccb_h.status = CAM_CMD_TIMEOUT;
+ goto error;
+ }
+
+ /* if a timeout occured, no sense */
+ if (vpo->vpo_error) {
+ if (vpo->vpo_error != VP0_ESELECT_TIMEOUT)
+ printf("vpo%d: VP0 error/timeout (%d)\n",
+ vpo->vpo_unit, vpo->vpo_error);
+
+ csio->ccb_h.status = CAM_CMD_TIMEOUT;
+ goto error;
+ }
+
+ /* check scsi status */
+ if (vpo->vpo_stat != SCSI_STATUS_OK) {
+ csio->scsi_status = vpo->vpo_stat;
+
+ /* check if we have to sense the drive */
+ if ((vpo->vpo_stat & SCSI_STATUS_CHECK_COND) != 0) {
+
+ vpo->vpo_sense.cmd.opcode = REQUEST_SENSE;
+ vpo->vpo_sense.cmd.length = csio->sense_len;
+ vpo->vpo_sense.cmd.control = 0;
+
+ if (vpo->vpo_isplus) {
+ errno = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
+ csio->ccb_h.target_id,
+ (char *)&vpo->vpo_sense.cmd,
+ sizeof(vpo->vpo_sense.cmd),
+ (char *)&csio->sense_data, csio->sense_len,
+ &vpo->vpo_sense.stat, &vpo->vpo_sense.count,
+ &vpo->vpo_error);
+ } else {
+ errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
+ csio->ccb_h.target_id,
+ (char *)&vpo->vpo_sense.cmd,
+ sizeof(vpo->vpo_sense.cmd),
+ (char *)&csio->sense_data, csio->sense_len,
+ &vpo->vpo_sense.stat, &vpo->vpo_sense.count,
+ &vpo->vpo_error);
+ }
+
+
+#ifdef VP0_DEBUG
+ printf("(sense) vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n",
+ errno, vpo->vpo_sense.stat, vpo->vpo_sense.count, vpo->vpo_error);
+#endif
+
+ /* check sense return status */
+ if (errno == 0 && vpo->vpo_sense.stat == SCSI_STATUS_OK) {
+ /* sense ok */
+ csio->ccb_h.status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR;
+ csio->sense_resid = csio->sense_len - vpo->vpo_sense.count;
+
+#ifdef VP0_DEBUG
+ /* dump of sense info */
+ printf("(sense) ");
+ for (i=0; i<vpo->vpo_sense.count; i++)
+ printf("%x ", ((char *)&csio->sense_data)[i]);
+ printf("\n");
+#endif
+
+ } else {
+ /* sense failed */
+ csio->ccb_h.status = CAM_AUTOSENSE_FAIL;
+ }
+ } else {
+ /* no sense */
+ csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+ }
+
+ goto error;
+ }
+
+ csio->resid = csio->dxfer_len - vpo->vpo_count;
+ csio->ccb_h.status = CAM_REQ_CMP;
+
+error:
+ splx(s);
+
+ return;
+}
+
+static void
+vpo_action(struct cam_sim *sim, union ccb *ccb)
+{
+
+ struct vpo_data *vpo = (struct vpo_data *)sim->softc;
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ {
+ struct ccb_scsiio *csio;
+
+ csio = &ccb->csio;
+
+#ifdef VP0_DEBUG
+ printf("vpo%d: XPT_SCSI_IO (0x%x) request\n",
+ vpo->vpo_unit, csio->cdb_io.cdb_bytes[0]);
+#endif
+
+ vpo_intr(vpo, csio);
+
+ xpt_done(ccb);
+
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ struct ccb_calc_geometry *ccg;
+
+ ccg = &ccb->ccg;
+
+#ifdef VP0_DEBUG
+ printf("vpo%d: XPT_CALC_GEOMETRY (bs=%d,vs=%d,c=%d,h=%d,spt=%d) request\n",
+ vpo->vpo_unit,
+ ccg->block_size,
+ ccg->volume_size,
+ ccg->cylinders,
+ ccg->heads,
+ ccg->secs_per_track);
+#endif
+
+ ccg->heads = 64;
+ ccg->secs_per_track = 32;
+ ccg->cylinders = ccg->volume_size /
+ (ccg->heads * ccg->secs_per_track);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_RESET_BUS: /* Reset the specified SCSI bus */
+ {
+
+#ifdef VP0_DEBUG
+ printf("vpo%d: XPT_RESET_BUS request\n", vpo->vpo_unit);
+#endif
+
+ if (vpo->vpo_isplus) {
+ if (imm_reset_bus(&vpo->vpo_io)) {
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ return;
+ }
+ } else {
+ if (vpoio_reset_bus(&vpo->vpo_io)) {
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ return;
+ }
+ }
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_PATH_INQ: /* Path routing inquiry */
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+#ifdef VP0_DEBUG
+ printf("vpo%d: XPT_PATH_INQ request\n", vpo->vpo_unit);
+#endif
+ cpi->version_num = 1; /* XXX??? */
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = 0;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = 7;
+ cpi->max_lun = 0;
+ cpi->initiator_id = VP0_INITIATOR;
+ cpi->bus_id = sim->bus_id;
+ cpi->base_transfer_speed = 93;
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Iomega", HBA_IDLEN);
+ strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN);
+ cpi->unit_number = sim->unit_number;
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+
+ return;
+}
+
+static void
+vpo_poll(struct cam_sim *sim)
+{
+ /* The ZIP is actually always polled throw vpo_action() */
+ return;
+}
+
+static devclass_t vpo_devclass;
+
+static device_method_t vpo_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, vpo_identify),
+ DEVMETHOD(device_probe, vpo_probe),
+ DEVMETHOD(device_attach, vpo_attach),
+
+ { 0, 0 }
+};
+
+static driver_t vpo_driver = {
+ "vpo",
+ vpo_methods,
+ sizeof(struct vpo_data),
+};
+DRIVER_MODULE(vpo, ppbus, vpo_driver, vpo_devclass, 0, 0);
diff --git a/sys/dev/ppbus/vpoio.c b/sys/dev/ppbus/vpoio.c
new file mode 100644
index 0000000..2a2a0dd
--- /dev/null
+++ b/sys/dev/ppbus/vpoio.c
@@ -0,0 +1,780 @@
+/*-
+ * Copyright (c) 1998, 1999 Nicolas Souchu
+ * Copyright (c) 2000 Alcove - 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <machine/clock.h>
+
+#endif
+
+#include "opt_vpo.h"
+
+#include <dev/ppbus/ppbio.h>
+#include <dev/ppbus/ppbconf.h>
+#include <dev/ppbus/ppb_msq.h>
+#include <dev/ppbus/vpoio.h>
+
+#include "ppbus_if.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 PERROR
+#define H_nERR n(PERROR)
+#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 WAIT_RET MS_PARAM(4, 2, MS_TYP_PTR)
+#define WAIT_TMO MS_PARAM(0, 0, MS_TYP_INT)
+
+#define DECLARE_WAIT_MICROSEQUENCE \
+struct ppb_microseq wait_microseq[] = { \
+ MS_SET(MS_UNKNOWN), \
+ /* loop */ \
+ MS_BRSET(nBUSY, 2 /* ready */), \
+ MS_DBRA(-2 /* loop */), \
+ MS_RET(1), /* timed out */ \
+ /* ready */ \
+ MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN), \
+ MS_RET(0) /* no error */ \
+}
+
+/* call this macro to initialize connect/disconnect microsequences */
+#define INIT_TRIG_MICROSEQ { \
+ int i; \
+ for (i=1; i <= 7; i+=2) { \
+ disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \
+ connect_epp_microseq[i].arg[2] = \
+ connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \
+ } \
+}
+
+#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
+static 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,MS_UNKNOWN /* c_pulse */)
+static 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
+};
+
+static 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)
+};
+
+static 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)
+};
+
+static 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);
+}
+
+#define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR)
+#define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR)
+#define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN)
+#define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR)
+
+/*
+ * 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())
+ */
+
+#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \
+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(-7 /* 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
+ */
+static 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(-4 /* 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
+ */
+static 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(-5 /* loop */),
+
+ /* return from the put call */
+ MS_RET(0)
+};
+
+/* EPP 1.7 microsequences, ptr and len set at runtime */
+static struct ppb_microseq epp17_outstr_body[] = {
+ MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
+
+/* loop: */
+ MS_RASSERT_P(1, MS_REG_EPP_D),
+ MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */
+ MS_DBRA(-3 /* 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)
+};
+
+static 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_D, MS_FETCH_ALL),
+ MS_BRSET(TIMEOUT, 3 /* error */), /* EPP timeout? */
+ MS_DBRA(-3 /* 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 struct ppb_microseq in_disk_mode[] = {
+ MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
+
+ MS_BRCLEAR(H_FLT, 3 /* error */),
+ MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_BRSET(H_FLT, 1 /* error */),
+
+ MS_RET(1),
+/* error: */
+ MS_RET(0)
+};
+
+static int
+vpoio_disconnect(struct vpoio_data *vpo)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int ret;
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
+ return (ppb_release_bus(ppbus, vpo->vpo_dev));
+}
+
+/*
+ * how : PPB_WAIT or PPB_DONTWAIT
+ */
+static int
+vpoio_connect(struct vpoio_data *vpo, int how)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error;
+ int ret;
+
+ if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) {
+
+#ifdef VP0_DEBUG
+ printf("%s: can't request bus!\n", __func__);
+#endif
+ return error;
+ }
+
+ if (PPB_IN_EPP_MODE(ppbus))
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
+ else
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
+
+ return (0);
+}
+
+/*
+ * vpoio_reset()
+ *
+ * SCSI reset signal, the drive must be in disk mode
+ */
+static void
+vpoio_reset (struct vpoio_data *vpo)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int ret;
+
+ struct ppb_microseq reset_microseq[] = {
+
+ #define INITIATOR MS_PARAM(0, 1, MS_TYP_INT)
+
+ MS_DASS(MS_UNKNOWN),
+ MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
+ MS_DELAY(25),
+ MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
+ MS_RET(0)
+ };
+
+ ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret);
+
+ return;
+}
+
+/*
+ * vpoio_in_disk_mode()
+ */
+static int
+vpoio_in_disk_mode(struct vpoio_data *vpo)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int ret;
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
+
+ return (ret);
+}
+
+/*
+ * vpoio_detect()
+ *
+ * Detect and initialise the VP0 adapter.
+ */
+static int
+vpoio_detect(struct vpoio_data *vpo)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error, ret;
+
+ /* allocate the bus, then apply microsequences */
+ if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
+ return (error);
+
+ /* Force disconnection */
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
+
+ /* Try to enter EPP mode, then connect to the drive in EPP mode */
+ if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
+ /* call manually the microseq instead of using the appropriate function
+ * since we already requested the ppbus */
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
+ }
+
+ /* If EPP mode switch failed or ZIP connection in EPP mode failed,
+ * try to connect in NIBBLE mode */
+ if (!vpoio_in_disk_mode(vpo)) {
+
+ /* The interface must be at least PS/2 or NIBBLE capable.
+ * There is no way to know if the ZIP will work with
+ * PS/2 mode since PS/2 and SPP both use the same connect
+ * sequence. One must supress PS/2 with boot flags if
+ * PS/2 mode fails (see ppc(4)).
+ */
+ if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
+ vpo->vpo_mode_found = VP0_MODE_PS2;
+ } else {
+ if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1)
+ goto error;
+
+ vpo->vpo_mode_found = VP0_MODE_NIBBLE;
+ }
+
+ /* Can't know if the interface is capable of PS/2 yet */
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
+ if (!vpoio_in_disk_mode(vpo)) {
+ vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
+ if (bootverbose)
+ printf("vpo%d: can't connect to the drive\n",
+ vpo->vpo_unit);
+
+ /* disconnect and release the bus */
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq,
+ &ret);
+ goto error;
+ }
+ } else {
+ vpo->vpo_mode_found = VP0_MODE_EPP;
+ }
+
+ /* send SCSI reset signal */
+ vpoio_reset(vpo);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
+
+ /* ensure we are disconnected or daisy chained peripheral
+ * may cause serious problem to the disk */
+ if (vpoio_in_disk_mode(vpo)) {
+ if (bootverbose)
+ printf("vpo%d: can't disconnect from the drive\n",
+ vpo->vpo_unit);
+ goto error;
+ }
+
+ ppb_release_bus(ppbus, vpo->vpo_dev);
+ return (0);
+
+error:
+ ppb_release_bus(ppbus, vpo->vpo_dev);
+ return (VP0_EINITFAILED);
+}
+
+/*
+ * vpoio_outstr()
+ */
+static int
+vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error = 0;
+
+ ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
+ (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
+
+ ppb_ecp_sync(ppbus);
+
+ return (error);
+}
+
+/*
+ * vpoio_instr()
+ */
+static int
+vpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error = 0;
+
+ ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
+ (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
+
+ ppb_ecp_sync(ppbus);
+
+ return (error);
+}
+
+static char
+vpoio_select(struct vpoio_data *vpo, int initiator, int target)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ 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, 2 /* ready */),
+ MS_DBRA(-2 /* 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(ppbus, 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)
+{
+ DECLARE_WAIT_MICROSEQUENCE;
+
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int ret, err;
+
+#if 0 /* broken */
+ if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR))
+ return (0);
+
+ return (ppb_rstr(ppbus) & 0xf0);
+#endif
+
+ /*
+ * 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
+ */
+
+ ppb_MS_init_msq(wait_microseq, 2,
+ WAIT_RET, (void *)&ret,
+ WAIT_TMO, tmo);
+
+ ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
+
+ if (err)
+ return (0); /* command timed out */
+
+ return(ret);
+}
+
+/*
+ * vpoio_probe()
+ *
+ * Low level probe of vpo device
+ *
+ */
+int
+vpoio_probe(device_t dev, struct vpoio_data *vpo)
+{
+ int error;
+
+ /* ppbus dependent initialisation */
+ vpo->vpo_dev = dev;
+
+ /*
+ * Initialize microsequence code
+ */
+ INIT_TRIG_MICROSEQ;
+
+ /* now, try to initialise the drive */
+ if ((error = vpoio_detect(vpo))) {
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * vpoio_attach()
+ *
+ * Low level attachment of vpo device
+ *
+ */
+int
+vpoio_attach(struct vpoio_data *vpo)
+{
+ DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ int error = 0;
+
+ vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
+ sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
+
+ if (!vpo->vpo_nibble_inbyte_msq)
+ return (ENXIO);
+
+ bcopy((void *)nibble_inbyte_submicroseq,
+ (void *)vpo->vpo_nibble_inbyte_msq,
+ sizeof(nibble_inbyte_submicroseq));
+
+ ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
+ INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
+ INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
+ INB_NIBBLE_F, nibble_inbyte_hook,
+ INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble);
+
+ /*
+ * Initialize mode dependent in/out microsequences
+ */
+ if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
+ goto error;
+
+ /* ppbus sets automatically the last mode entered during detection */
+ switch (vpo->vpo_mode_found) {
+ case VP0_MODE_EPP:
+ ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body);
+ ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body);
+ printf("vpo%d: EPP mode\n", vpo->vpo_unit);
+ break;
+ case VP0_MODE_PS2:
+ ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
+ ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
+ printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
+ break;
+ case VP0_MODE_NIBBLE:
+ ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
+ ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
+ printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
+ break;
+ default:
+ panic("vpo: unknown mode %d", vpo->vpo_mode_found);
+ }
+
+ ppb_release_bus(ppbus, vpo->vpo_dev);
+
+error:
+ return (error);
+}
+
+/*
+ * 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)) {
+
+#ifdef VP0_DEBUG
+ printf("%s: not in disk mode!\n", __func__);
+#endif
+ /* 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)
+{
+ device_t ppbus = device_get_parent(vpo->vpo_dev);
+ 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(ppbus, 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;
+ }
+
+ /* if in EPP mode or writing bytes, try to transfer a sector
+ * otherwise, just send one byte
+ */
+ if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0)
+ len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
+ VP0_SECTOR_SIZE : 1;
+ else
+ len = 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/vpoio.h b/sys/dev/ppbus/vpoio.h
new file mode 100644
index 0000000..fe66fa5
--- /dev/null
+++ b/sys/dev/ppbus/vpoio.h
@@ -0,0 +1,100 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#ifndef __VP0IO_H
+#define __VP0IO_H
+
+/*
+ * The ZIP drive cannot act as an initiator.
+ */
+#define VP0_INITIATOR 0x7
+
+#define VP0_ESELECT_TIMEOUT 1
+#define VP0_ECMD_TIMEOUT 2
+#define VP0_ECONNECT 3
+#define VP0_ESTATUS_TIMEOUT 4
+#define VP0_EDATA_OVERFLOW 5
+#define VP0_EDISCONNECT 6
+#define VP0_EPPDATA_TIMEOUT 7
+#define VP0_ENEGOCIATE 8
+#define VP0_ENOPORT 9
+#define VP0_EINITFAILED 10
+#define VP0_EINTR 12
+
+#define VP0_EOTHER 13
+
+#define VP0_OPENNINGS 1
+
+/*
+ * Data structure used during microsequence execution
+ * when characters are received in nibble mode
+ */
+struct vpo_nibble {
+ char h; /* most significant nibble */
+ char l; /* less significant nibble */
+};
+
+/* Mode found during initialisation */
+#define VP0_MODE_UNDEFINED 0x0
+#define VP0_MODE_NIBBLE 0x1
+#define VP0_MODE_PS2 0x2
+#define VP0_MODE_EPP 0x3
+
+struct vpoio_data {
+ unsigned short int vpo_unit;
+ int vpo_mode_found; /* Mode found during init */
+
+ struct vpo_nibble vpo_nibble;
+
+ /* each device must have its own nibble inbyte microsequence */
+ struct ppb_microseq *vpo_nibble_inbyte_msq;
+
+ device_t vpo_dev;
+};
+
+#define vpoio_set_unit(vpo,unit) ((vpo)->vpo_unit = unit)
+
+int vpoio_probe(device_t dev, struct vpoio_data *vpo);
+
+int vpoio_attach(struct vpoio_data *vpo);
+int vpoio_reset_bus(struct vpoio_data *vpo);
+
+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);
+
+int imm_probe(device_t dev, struct vpoio_data *vpo);
+
+int imm_attach(struct vpoio_data *vpo);
+int imm_reset_bus(struct vpoio_data *vpo);
+
+int imm_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