summaryrefslogtreecommitdiff
path: root/sys/arch/pc532/dev/lpt.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/pc532/dev/lpt.c
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/pc532/dev/lpt.c')
-rw-r--r--sys/arch/pc532/dev/lpt.c876
1 files changed, 876 insertions, 0 deletions
diff --git a/sys/arch/pc532/dev/lpt.c b/sys/arch/pc532/dev/lpt.c
new file mode 100644
index 00000000000..9811279385f
--- /dev/null
+++ b/sys/arch/pc532/dev/lpt.c
@@ -0,0 +1,876 @@
+/* $NetBSD: lpt.c,v 1.6 1995/09/26 20:16:06 phil Exp $ */
+
+/*
+ * Copyright (c) 1994 Matthias Pfaller.
+ * Copyright (c) 1994 Poul-Henning Kamp
+ * Copyright (c) 1993, 1994 Charles Hannum.
+ * 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.
+ *
+ */
+
+/*
+ * Device Driver for Matthias's parallel printer port.
+ * This driver is based on the i386 lpt driver and
+ * some IP code from Poul-Henning Kamp.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/device.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+#include <machine/cpu.h>
+
+#include "lpt.h"
+#include "lptreg.h"
+
+#if defined(INET) && defined(PLIP)
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#define LPT_INVERT (LPC_NBUSY|LPC_NERROR|LPC_NACK|LPC_ONLINE)
+#define LPT_MASK (LPC_NBUSY|LPC_NERROR|LPC_NACK|LPC_NOPAPER|LPC_ONLINE)
+
+#define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */
+#define STEP hz/4
+
+#define LPTPRI (PZERO+8)
+#define LPT_BSIZE 1024
+
+#if defined(INET) && defined(PLIP)
+#ifndef PLIPMTU /* MTU for the plip# interfaces */
+#if defined(COMPAT_PLIP10)
+#define PLIPMTU 1600
+#else
+#define PLIPMTU (ETHERMTU - ifp->if_hdrlen)
+#endif
+#endif
+
+#ifndef PLIPMXSPIN1 /* DELAY factor for the plip# interfaces */
+#define PLIPMXSPIN1 2000 /* Spinning for remote intr to happen */
+#endif
+
+#ifndef PLIPMXSPIN2 /* DELAY factor for the plip# interfaces */
+#define PLIPMXSPIN2 6000 /* Spinning for remote handshake to happen */
+#endif
+
+#ifndef PLIPMXERRS /* Max errors before !RUNNING */
+#define PLIPMXERRS 20
+#endif
+#ifndef PLIPMXRETRY
+#define PLIPMXRETRY 20 /* Max number of retransmits */
+#endif
+#ifndef PLIPRETRY
+#define PLIPRETRY hz/50 /* Time between retransmits */
+#endif
+#endif
+
+struct lpt_softc {
+ struct device sc_dev;
+ size_t sc_count;
+ u_char *sc_inbuf;
+ u_char *sc_cp;
+ volatile struct i8255 *sc_i8255;
+ int sc_irq;
+ u_char sc_state;
+#define LPT_OPEN 0x01 /* device is open */
+#define LPT_INIT 0x02 /* waiting to initialize for open */
+
+ u_char sc_status;
+ u_char sc_flags;
+#define LPT_AUTOLF 0x20 /* automatic LF on CR */
+#define LPT_NOPRIME 0x40 /* don't prime on open */
+
+#if defined(INET) && defined(PLIP)
+ struct arpcom sc_arpcom;
+ u_char *sc_ifbuf;
+ int sc_ifierrs; /* consecutive input errors */
+ int sc_ifoerrs; /* consecutive output errors */
+ int sc_ifsoftint; /* i/o software interrupt */
+ volatile int sc_pending; /* interrputs pending */
+#define PLIP_IPENDING 1
+#define PLIP_OPENDING 2
+
+#if defined(COMPAT_PLIP10)
+ u_char sc_adrcksum;
+#endif
+#endif
+};
+
+#define LPTUNIT(s) (minor(s) & 0x1f)
+#define LPTFLAGS(s) (minor(s) & 0xe0)
+
+static int lptmatch(struct device *, void *, void *aux);
+static void lptattach(struct device *, struct device *, void *);
+static void lptintr(struct lpt_softc *);
+static int notready(u_char, struct lpt_softc *);
+static void lptout(void *arg);
+static int pushbytes(struct lpt_softc *);
+
+#if defined(INET) && defined(PLIP)
+/* Functions for the plip# interface */
+static void plipattach(struct lpt_softc *,int);
+static int plipioctl(struct ifnet *, u_long, caddr_t);
+static void plipsoftint(struct lpt_softc *);
+static void plipinput(struct lpt_softc *);
+static void plipstart(struct ifnet *);
+static void plipoutput(struct lpt_softc *);
+#endif
+
+struct cfdriver lptcd = {
+ NULL,
+ "lpt",
+ lptmatch,
+ lptattach,
+ DV_TTY,
+ sizeof(struct lpt_softc),
+ NULL,
+ 0
+};
+
+lptmatch(struct device *parent, void *cf, void *aux)
+{
+ volatile struct i8255 *i8255 =
+ (volatile struct i8255 *)((struct cfdata *)cf)->cf_loc[0];
+ int unit = ((struct cfdata *)cf)->cf_unit;
+
+ if (unit >= LPT_MAX)
+ return(0);
+
+ if ((int) i8255 == -1)
+ i8255 = LPT_ADR(unit);
+
+ i8255->port_control = LPT_PROBE_MODE;
+
+ i8255->port_control = LPT_PROBE_CLR;
+ if ((i8255->port_c & LPT_PROBE_MASK) != 0)
+ return 0;
+
+ i8255->port_control = LPT_PROBE_SET;
+ if ((i8255->port_c & LPT_PROBE_MASK) == 0)
+ return 0;
+
+ i8255->port_control = LPT_PROBE_CLR;
+ if ((i8255->port_c & LPT_PROBE_MASK) != 0)
+ return 0;
+
+ i8255->port_control = LPT_MODE;
+ i8255->port_a = LPA_ACTIVE | LPA_NPRIME;
+
+ return 1;
+}
+
+void
+lptattach(struct device *parent, struct device *self, void *aux)
+{
+ struct lpt_softc *sc = (struct lpt_softc *) self;
+ volatile struct i8255 *i8255 =
+ (volatile struct i8255 *)self->dv_cfdata->cf_loc[0];
+
+ if ((sc->sc_irq = self->dv_cfdata->cf_loc[1]) == -1)
+ sc->sc_irq = LPT_IRQ(self->dv_unit);
+
+ if ((int)i8255 == -1)
+ i8255 = LPT_ADR(self->dv_unit);
+ i8255->port_control = LPT_MODE;
+ i8255->port_a = LPA_ACTIVE | LPA_NPRIME;
+ i8255->port_control = LPT_IRQDISABLE;
+
+ sc->sc_state = 0;
+ sc->sc_i8255 = i8255;
+
+#if defined(INET) && defined(PLIP)
+ plipattach(sc, self->dv_unit);
+#endif
+ intr_establish(sc->sc_irq, lptintr, sc, sc->sc_dev.dv_xname,
+ IPL_NONE, FALLING_EDGE);
+ printf(" addr 0x%x, irq %d\n", (int) i8255, sc->sc_irq);
+}
+
+/*
+ * Reset the printer, then wait until it's selected and not busy.
+ */
+int
+lptopen(dev_t dev, int flag)
+{
+ struct lpt_softc *sc = (struct lpt_softc *) lptcd.cd_devs[LPTUNIT(dev)];
+ volatile struct i8255 *i8255 = sc->sc_i8255;
+ u_char flags = LPTFLAGS(dev);
+ int error;
+ int spin;
+
+ if (LPTUNIT(dev) >= NLPT || !sc)
+ return ENXIO;
+
+ if (sc->sc_state)
+ return EBUSY;
+
+#if defined(INET) && defined(PLIP)
+ if (sc->sc_arpcom.ac_if.if_flags & IFF_UP)
+ return EBUSY;
+#endif
+
+ sc->sc_state = LPT_INIT;
+ sc->sc_flags = flags;
+
+ if ((flags & LPT_NOPRIME) == 0) {
+ /* assert INIT for 100 usec to start up printer */
+ i8255->port_a &= ~LPA_NPRIME;
+ DELAY(100);
+ }
+
+ if (flags & LPT_AUTOLF)
+ i8255->port_a |= LPA_ALF | LPA_SELECT | LPA_NPRIME;
+ else
+ i8255->port_a = (i8255->port_a & ~LPA_ALF)
+ | LPA_SELECT | LPA_NPRIME;
+
+ /* wait till ready (printer running diagnostics) */
+ for (spin = 0; notready(i8255->port_c, sc); spin += STEP) {
+ if (spin >= TIMEOUT) {
+ sc->sc_state = 0;
+ return EBUSY;
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ if (error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen",
+ STEP) != EWOULDBLOCK) {
+ sc->sc_state = 0;
+ return error;
+ }
+ }
+
+ sc->sc_inbuf = malloc(LPT_BSIZE, M_DEVBUF, M_WAITOK);
+ sc->sc_status =
+ sc->sc_count = 0;
+ sc->sc_state = LPT_OPEN;
+
+ return 0;
+}
+
+int
+notready(u_char status, struct lpt_softc *sc)
+{
+ status ^= LPT_INVERT;
+
+ if (status != sc->sc_status) {
+ if (status & LPC_NOPAPER)
+ log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
+ if (status & LPC_ONLINE)
+ log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
+ if (status & LPC_NERROR)
+ log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
+ if (status & LPC_NACK)
+ log(LOG_NOTICE, "%s: NACK low\n", sc->sc_dev.dv_xname);
+ if (status & LPC_NBUSY)
+ log(LOG_NOTICE, "%s: NBUSY low\n", sc->sc_dev.dv_xname);
+ sc->sc_status = status;
+ }
+ return status & LPT_MASK;
+}
+
+void
+lptout(void *arg)
+{
+ struct lpt_softc *sc = (struct lpt_softc *) arg;
+ if (sc->sc_count > 0)
+ sc->sc_i8255->port_control = LPT_IRQENABLE;
+}
+
+/*
+ * Close the device, and free the local line buffer.
+ */
+lptclose(dev_t dev, int flag)
+{
+ struct lpt_softc *sc = (struct lpt_softc *) lptcd.cd_devs[LPTUNIT(dev)];
+
+ if (sc->sc_count)
+ (void) pushbytes(sc);
+
+ sc->sc_i8255->port_control = LPT_IRQDISABLE;
+ sc->sc_state = 0;
+ free(sc->sc_inbuf, M_DEVBUF);
+
+ return 0;
+}
+
+int
+pushbytes(struct lpt_softc *sc)
+{
+ volatile struct i8255 *i8255 = sc->sc_i8255;
+ int error;
+
+ while (sc->sc_count > 0) {
+ i8255->port_control = LPT_IRQENABLE;
+ if (error = tsleep((caddr_t)sc, LPTPRI | PCATCH,
+ "lptwrite", 0))
+ return error;
+ }
+ return 0;
+}
+
+/*
+ * Copy a line from user space to a local buffer, then call pushbytes to
+ * get the chars moved to the output queue.
+ */
+lptwrite(dev_t dev, struct uio *uio)
+{
+ struct lpt_softc *sc = (struct lpt_softc *) lptcd.cd_devs[LPTUNIT(dev)];
+ size_t n;
+ int error = 0;
+
+ if (sc->sc_count) return EBUSY;
+ while (n = min(LPT_BSIZE, uio->uio_resid)) {
+ uiomove(sc->sc_cp = sc->sc_inbuf, n, uio);
+ sc->sc_count = n;
+ error = pushbytes(sc);
+ if (error) {
+ /*
+ * Return accurate residual if interrupted or timed
+ * out.
+ */
+ uio->uio_resid += sc->sc_count;
+ sc->sc_count = 0;
+ return error;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Handle printer interrupts which occur when the printer is ready to accept
+ * another char.
+ */
+void
+lptintr(struct lpt_softc *sc)
+{
+ volatile struct i8255 *i8255 = sc->sc_i8255;
+
+#if defined(INET) && defined(PLIP)
+ if(sc->sc_arpcom.ac_if.if_flags & IFF_UP) {
+ i8255->port_a &= ~LPA_ACKENABLE;
+ sc->sc_pending |= PLIP_IPENDING;
+ softintr(sc->sc_ifsoftint);
+ return;
+ }
+#endif
+
+ if ((sc->sc_state & LPT_OPEN) == 0) {
+ i8255->port_control = LPT_IRQDISABLE;
+ return;
+ }
+
+ if (sc->sc_count) {
+ /* is printer online and ready for output? */
+ if (notready(i8255->port_c, sc)) {
+ i8255->port_control = LPT_IRQDISABLE;
+ timeout(lptout, sc, STEP);
+ return;
+ }
+ /* send char */
+ i8255->port_a &= ~LPA_ACTIVE;
+ i8255->port_b = *sc->sc_cp++;
+ i8255->port_a |= LPA_ACTIVE;
+ sc->sc_count--;
+ }
+
+ if (sc->sc_count == 0) {
+ /* none, wake up the top half to get more */
+ i8255->port_control = LPT_IRQDISABLE;
+ wakeup((caddr_t)sc);
+ }
+}
+
+int
+lptioctl(dev_t dev, int cmd, caddr_t data, int flag)
+{
+ int error = 0;
+
+ switch (cmd) {
+ default:
+ error = EINVAL;
+ }
+
+ return error;
+}
+
+#if defined(INET) && defined(PLIP)
+
+static void
+plipattach(struct lpt_softc *sc, int unit)
+{
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+
+ sc->sc_ifbuf = NULL;
+ ifp->if_unit = unit;
+ ifp->if_name = "plip";
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
+ ifp->if_output = ether_output;
+ ifp->if_start = plipstart;
+ ifp->if_ioctl = plipioctl;
+ ifp->if_watchdog = 0;
+
+ ifp->if_type = IFT_ETHER;
+ ifp->if_addrlen = 6;
+ ifp->if_hdrlen = 14;
+ ifp->if_mtu = PLIPMTU;
+ sc->sc_ifsoftint = intr_establish(SOFTINT, plipsoftint, sc,
+ sc->sc_dev.dv_xname, IPL_NET, 0);
+
+ if_attach(ifp);
+}
+
+/*
+ * Process an ioctl request.
+ */
+static int
+plipioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct proc *p = curproc;
+ struct lpt_softc *sc = (struct lpt_softc *) lptcd.cd_devs[ifp->if_unit];
+ volatile struct i8255 *i8255 = sc->sc_i8255;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s;
+ int error = 0;
+
+ switch (cmd) {
+
+ case SIOCSIFFLAGS:
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ sc->sc_i8255->port_control = LPT_MODE;
+ i8255->port_a = LPA_ACTIVE | LPA_NPRIME;
+ if (sc->sc_ifbuf)
+ free(sc->sc_ifbuf, M_DEVBUF);
+ sc->sc_ifbuf = NULL;
+ }
+ if (((ifp->if_flags & IFF_UP)) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0)) {
+ if (sc->sc_state) {
+ error = EBUSY;
+ break;
+ }
+ if (!sc->sc_ifbuf)
+ sc->sc_ifbuf =
+ malloc(ifp->if_mtu + ifp->if_hdrlen,
+ M_DEVBUF, M_WAITOK);
+ ifp->if_flags |= IFF_RUNNING;
+ sc->sc_i8255->port_control = LPT_IRQDISABLE;
+ sc->sc_i8255->port_b = 0;
+ sc->sc_i8255->port_a |= LPA_ACKENABLE;
+ }
+ break;
+
+ case SIOCSIFADDR:
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ if (!sc->sc_ifbuf)
+ sc->sc_ifbuf =
+ malloc(PLIPMTU + ifp->if_hdrlen,
+ M_DEVBUF, M_WAITOK);
+ sc->sc_arpcom.ac_enaddr[0] = 0xfc;
+ sc->sc_arpcom.ac_enaddr[1] = 0xfc;
+ bcopy((caddr_t)&IA_SIN(ifa)->sin_addr,
+ (caddr_t)&sc->sc_arpcom.ac_enaddr[2], 4);
+ sc->sc_arpcom.ac_ipaddr = IA_SIN(ifa)->sin_addr;
+#if defined(COMPAT_PLIP10)
+ if (ifp->if_flags & IFF_LINK0) {
+ int i;
+ sc->sc_arpcom.ac_enaddr[0] = 0xfd;
+ sc->sc_arpcom.ac_enaddr[1] = 0xfd;
+ for (i = sc->sc_adrcksum = 0; i < 5; i++)
+ sc->sc_adrcksum += sc->sc_arpcom.ac_enaddr[i];
+ sc->sc_adrcksum *= 2;
+ }
+#endif
+ ifp->if_flags |= IFF_RUNNING | IFF_UP;
+#if 0
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ struct sockaddr_dl *sdl;
+ if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) &&
+ sdl->sdl_family == AF_LINK) {
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ifp->if_addrlen;
+ bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr,
+ LLADDR(sdl), ifp->if_addrlen);
+ break;
+ }
+ }
+#endif
+ sc->sc_i8255->port_control = LPT_IRQDISABLE;
+ sc->sc_i8255->port_b = 0;
+ sc->sc_i8255->port_a |= LPA_ACKENABLE;
+ arp_ifinit(&sc->sc_arpcom, ifa);
+ } else
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCAIFADDR:
+ case SIOCDIFADDR:
+ case SIOCSIFDSTADDR:
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCSIFMTU:
+ if ((error = suser(p->p_ucred, &p->p_acflag)))
+ return(error);
+ if (ifp->if_mtu != ifr->ifr_metric) {
+ ifp->if_mtu = ifr->ifr_metric;
+ if (sc->sc_ifbuf) {
+ s = splimp();
+ free(sc->sc_ifbuf, M_DEVBUF);
+ sc->sc_ifbuf =
+ malloc(ifp->if_mtu + ifp->if_hdrlen,
+ M_DEVBUF, M_WAITOK);
+ splx(s);
+ }
+ }
+ break;
+
+ case SIOCGIFMTU:
+ ifr->ifr_metric = ifp->if_mtu;
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+static void
+plipsoftint(struct lpt_softc *sc)
+{
+ int pending = sc->sc_pending;
+
+ while (sc->sc_pending & PLIP_IPENDING) {
+ pending |= sc->sc_pending;
+ sc->sc_pending = 0;
+ plipinput(sc);
+ }
+
+ if (pending & PLIP_OPENDING)
+ plipoutput(sc);
+}
+
+static int
+plipreceive(volatile struct i8255 *i8255, u_char *buf, int len)
+{
+ int i;
+ u_char cksum = 0, c;
+
+ while (len--) {
+ i = PLIPMXSPIN2;
+ while ((i8255->port_c & LPC_NBUSY) != 0)
+ if (i-- < 0) return -1;
+ c = i8255->port_c >> 4;
+ i8255->port_b = 0x11;
+ while ((i8255->port_c & LPC_NBUSY) == 0)
+ if (i-- < 0) return -1;
+ c |= i8255->port_c & 0xf0;
+ i8255->port_b = 0x01;
+ cksum += (*buf++ = c);
+ }
+ return(cksum);
+}
+
+static void
+plipinput(struct lpt_softc *sc)
+{
+ extern struct mbuf *m_devget(char *, int, int, struct ifnet *, void (*)());
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ volatile struct i8255 *i8255 = sc->sc_i8255;
+ struct mbuf *m;
+ struct ether_header *eh;
+ u_char *p = sc->sc_ifbuf, minibuf[4];
+ int c, i = 0, s, len, cksum;
+
+ if (!(i8255->port_c & LPC_NACK)) {
+ i8255->port_a |= LPA_ACKENABLE;
+ ifp->if_collisions++;
+ return;
+ }
+ i8255->port_b = 0x01;
+ i8255->port_a &= ~(LPA_ACKENABLE | LPA_ACTIVE);
+
+#if defined(COMPAT_PLIP10)
+ if (ifp->if_flags & IFF_LINK0) {
+ if (plipreceive(i8255, minibuf, 3) < 0) goto err;
+ len = (minibuf[1] << 8) | minibuf[2];
+ if (len > (ifp->if_mtu + ifp->if_hdrlen)) goto err;
+
+ switch (minibuf[0]) {
+ case 0xfc:
+ p[0] = p[ 6] = ifp->ac_enaddr[0];
+ p[1] = p[ 7] = ifp->ac_enaddr[1];
+ p[2] = p[ 8] = ifp->ac_enaddr[2];
+ p[3] = p[ 9] = ifp->ac_enaddr[3];
+ p[4] = p[10] = ifp->ac_enaddr[4];
+ p += 5;
+ if ((cksum = plipreceive(i8255, p, 1)) < 0) goto err;
+ p += 6;
+ if ((c = plipreceive(i8255, p, len - 11)) < 0) goto err;
+ cksum += c + sc->sc_adrcksum;
+ c = p[1]; p[1] = p[2]; p[2] = c;
+ cksum &= 0xff;
+ break;
+ case 0xfd:
+ if ((cksum = plipreceive(i8255, p, len)) < 0) goto err;
+ break;
+ default:
+ goto err;
+ }
+ } else
+#endif
+ {
+ if (plipreceive(i8255, minibuf, 2) < 0) goto err;
+ len = (minibuf[1] << 8) | minibuf[0];
+ if (len > (ifp->if_mtu + ifp->if_hdrlen)) {
+ log(LOG_NOTICE, "plip%d: packet > MTU\n", ifp->if_unit);
+ goto err;
+ }
+ if ((cksum = plipreceive(i8255, p, len)) < 0) goto err;
+ }
+
+ if (plipreceive(i8255, minibuf, 1) < 0) goto err;
+ if (cksum != minibuf[0]) {
+ log(LOG_NOTICE, "plip%d: checksum error\n", ifp->if_unit);
+ goto err;
+ }
+ i8255->port_b = 0x00;
+
+ s = splimp();
+ if (m = m_devget(sc->sc_ifbuf, len, 0, ifp, NULL)) {
+ /* We assume that the header fit entirely in one mbuf. */
+ eh = mtod(m, struct ether_header *);
+ m->m_pkthdr.len -= sizeof(*eh);
+ m->m_len -= sizeof(*eh);
+ m->m_data += sizeof(*eh);
+ ether_input(ifp, eh, m);
+ }
+ splx(s);
+ sc->sc_ifierrs = 0;
+ ifp->if_ipackets++;
+ i8255->port_a |= LPA_ACKENABLE | LPA_ACTIVE;
+ return;
+
+err:
+ i8255->port_b = 0x00;
+
+ if (sc->sc_ifierrs < PLIPMXERRS) {
+ i8255->port_a |= LPA_ACKENABLE | LPA_ACTIVE;
+ } else {
+ /* We are not able to send receive anything for now,
+ * so stop wasting our time and leave the interrupt
+ * disabled.
+ */
+ if (sc->sc_ifierrs == PLIPMXERRS)
+ log(LOG_NOTICE, "plip%d: rx hard error\n", ifp->if_unit);
+ i8255->port_a |= LPA_ACTIVE;
+ }
+ ifp->if_ierrors++;
+ sc->sc_ifierrs++;
+ return;
+}
+
+static int
+pliptransmit(volatile struct i8255 *i8255, u_char *buf, int len)
+{
+ int i;
+ u_char cksum = 0, c;
+
+ while (len--) {
+ i = PLIPMXSPIN2;
+ cksum += (c = *buf++);
+ while ((i8255->port_c & LPC_NBUSY) == 0)
+ if (i-- < 0) return -1;
+ i8255->port_b = c & 0x0f;
+ i8255->port_b = c & 0x0f | 0x10;
+ c >>= 4;
+ while ((i8255->port_c & LPC_NBUSY) != 0)
+ if (i-- < 0) return -1;
+ i8255->port_b = c | 0x10;
+ i8255->port_b = c;
+ }
+ return(cksum);
+}
+
+/*
+ * Setup output on interface.
+ */
+static void
+plipstart(struct ifnet *ifp)
+{
+ struct lpt_softc *sc = (struct lpt_softc *) lptcd.cd_devs[ifp->if_unit];
+ sc->sc_pending |= PLIP_OPENDING;
+ softintr(sc->sc_ifsoftint);
+}
+
+static void
+plipoutput(struct lpt_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ volatile struct i8255 *i8255 = sc->sc_i8255;
+ struct mbuf *m0, *m;
+ u_char minibuf[4], cksum;
+ int len, i, s;
+
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
+ ifp->if_flags |= IFF_OACTIVE;
+
+ if (sc->sc_ifoerrs)
+ untimeout((void (*)(void *))plipoutput, sc);
+
+ for (;;) {
+ s = splnet();
+ IF_DEQUEUE(&ifp->if_snd, m0);
+ splx(s);
+ if (!m0)
+ break;
+
+ for (len = 0, m = m0; m; m = m->m_next)
+ len += m->m_len;
+#if defined(COMPAT_PLIP10)
+ if (ifp->if_flags & IFF_LINK0) {
+ minibuf[0] = 3;
+ minibuf[1] = 0xfd;
+ minibuf[2] = len >> 8;
+ minibuf[3] = len;
+ } else
+#endif
+ {
+ minibuf[0] = 2;
+ minibuf[1] = len;
+ minibuf[2] = len >> 8;
+ }
+
+ /* Trigger remote interrupt */
+ i = PLIPMXSPIN1;
+ do {
+ if (sc->sc_pending & PLIP_IPENDING) {
+ i8255->port_b = 0x00;
+ sc->sc_pending = 0;
+ plipinput(sc);
+ i = PLIPMXSPIN1;
+ } else if (i-- < 0)
+ goto retry;
+ /* Retrigger remote interrupt */
+ i8255->port_b = 0x08;
+ } while ((i8255->port_c & LPC_NERROR) == 0);
+ i8255->port_a &= ~(LPA_ACKENABLE | LPA_ACTIVE);
+
+ if (pliptransmit(i8255, minibuf + 1, minibuf[0]) < 0) goto retry;
+ for (cksum = 0, m = m0; m; m = m->m_next) {
+ i = pliptransmit(i8255, mtod(m, u_char *), m->m_len);
+ if (i < 0) goto retry;
+ cksum += i;
+ }
+ if (pliptransmit(i8255, &cksum, 1) < 0) goto retry;
+ i = PLIPMXSPIN2;
+ while ((i8255->port_c & LPC_NBUSY) == 0)
+ if (i-- < 0) goto retry;
+ i8255->port_b = 0x00;
+
+ ifp->if_opackets++;
+ ifp->if_obytes += len + 4;
+ sc->sc_ifoerrs = 0;
+ s = splimp();
+ m_freem(m0);
+ splx(s);
+ i8255->port_a |= LPA_ACKENABLE;
+ }
+ i8255->port_a |= LPA_ACTIVE;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+
+retry:
+ if (i8255->port_c & LPC_NACK)
+ ifp->if_collisions++;
+ else
+ ifp->if_oerrors++;
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ i8255->port_b = 0x00;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == (IFF_RUNNING | IFF_UP)
+ && sc->sc_ifoerrs < PLIPMXRETRY) {
+ s = splnet();
+ IF_PREPEND(&ifp->if_snd, m0);
+ splx(s);
+ i8255->port_a |= LPA_ACKENABLE | LPA_ACTIVE;
+ timeout((void (*)(void *))plipoutput, sc, PLIPRETRY);
+ } else {
+ if (sc->sc_ifoerrs == PLIPMXRETRY) {
+ log(LOG_NOTICE, "plip%d: tx hard error\n", ifp->if_unit);
+ }
+ s = splimp();
+ m_freem(m0);
+ splx(s);
+ i8255->port_a |= LPA_ACTIVE;
+ }
+ sc->sc_ifoerrs++;
+}
+
+#endif