Subject: port-i386/1516: lpt.c does not support plip
To: None <gnats-bugs@gnats.netbsd.org>
From: Matthias Pfaller <leo@robin.dachau.marco.de>
List: netbsd-bugs
Date: 09/26/1995 13:28:54
>Number:         1516
>Category:       port-i386
>Synopsis:       lpt.c does not support plip
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Sep 26 08:35:01 1995
>Last-Modified:
>Originator:     Matthias Pfaller
>Organization:
leo@dachau.marco.de			in real life: Matthias Pfaller
marco GmbH, 85221 Dachau, Germany	tel: +49 8131 516142
>Release:        95/08/26
>Environment:
	
System: NetBSD robin 1.0A NetBSD 1.0A (ROBIN) #5: Sat Sep 16 16:17:41 MSZ 1995 root@robin:/usr/src/sys/arch/i386/compile/ROBIN i386


>Description:
	
	plip (ip over parallel lines) is not supported by lpt.c on the i386.
>How-To-Repeat:
	ifconfig plip0 :-)
>Fix:

What follows is a port of my pc532 code done by Martin Husemann
(martin@laurin.teuto.de). I upgraded it to the current version of my
code. I use this to connect a linux box to a pc532 running NetBSD. I
don't know how well this works when you connect two NetBSD boxes. There
is still one problem remaining: I don't know how one gets a new software
interrupt on the i386. Could someone please try this code on an i386 and
finish the missing parts?

--- lpt.c.orig	Tue Sep 26 11:30:29 1995
+++ lpt.c	Thu Sep 14 22:42:19 1995
@@ -1,6 +1,8 @@
 /*	$NetBSD: lpt.c,v 1.30 1995/04/17 12:09:17 cgd 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.
@@ -64,6 +66,20 @@
 #include <sys/device.h>
 #include <sys/syslog.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
+
 #include <machine/cpu.h>
 #include <machine/pio.h>
 
@@ -76,6 +92,34 @@
 #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	4000	/* Spinning for remote intr to happen */
+#endif
+
+#ifndef PLIPMXSPIN2		/* DELAY factor for the plip# interfaces */
+#define	PLIPMXSPIN2	12000	/* Spinning for remote handshake to happen */
+#endif
+
+#ifndef PLIPMXERRS		/* Max errors before !RUNNING */
+#define	PLIPMXERRS	50
+#endif
+#ifndef PLIPMXRETRY
+#define PLIPMXRETRY	20	/* Max number of retransmits */
+#endif
+#ifndef PLIPRETRY
+#define PLIPRETRY	hz/50	/* Time between retransmits */
+#endif
+#endif
+
 #if !defined(DEBUG) || !defined(notdef)
 #define lprintf
 #else
@@ -103,12 +147,37 @@
 #define	LPT_NOINTR	0x80	/* do not use interrupt */
 	u_char sc_control;
 	u_char sc_laststatus;
+
+#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
 };
 
 int lptprobe __P((struct device *, void *, void *));
 void lptattach __P((struct device *, struct device *, void *));
 int lptintr __P((void *));
 
+#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", lptprobe, lptattach, DV_TTY, sizeof(struct lpt_softc)
 };
@@ -237,6 +306,9 @@
 	sc->sc_state = 0;
 	outb(iobase + lpt_control, LPC_NINIT);
 
+#if defined(INET) && defined(PLIP)
+	plipattach(sc, self->dv_unit);
+#endif
 	if (ia->ia_irq != IRQUNK)
 		sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE,
 		    ISA_IPL_NONE, lptintr, sc);
@@ -276,6 +348,11 @@
 	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;
 	lprintf("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags);
@@ -485,6 +562,15 @@
 	struct lpt_softc *sc = arg;
 	int iobase = sc->sc_iobase;
 
+#if defined(INET) && defined(PLIP)
+	if(sc->sc_arpcom.ac_if.if_flags & IFF_UP) {
+		outb(iobase + lpt_control, sc->sc_control &= ~LPC_IENABLE);
+		sc->sc_pending |= PLIP_IPENDING;
+		softintr(sc->sc_ifsoftint);
+		return;
+	}
+#endif
+
 #if 0
 	if ((sc->sc_state & LPT_OPEN) == 0)
 		return 0;
@@ -529,3 +615,438 @@
 
 	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;
+
+#error How would establish a software interrupt on the i386?
+
+	sc->sc_ifsoftint = isa_intr_establish(SOFTINT, 0, ISA_IPL_NET,
+				plipsoftint, sc);
+
+	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];
+	int iobase = sc->sc_iobase;
+	u_char control = sc->sc_control;
+	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;
+			control = LPC_SELECT | LPC_NINIT;
+			outb(iobase + lpt_control, control);
+
+			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_ih == 0) {
+				error = EINVAL;
+				break;
+			}
+			if (!sc->sc_ifbuf)
+				sc->sc_ifbuf =
+					malloc(ifp->if_mtu + ifp->if_hdrlen,
+					       M_DEVBUF, M_WAITOK);
+		        ifp->if_flags |= IFF_RUNNING;
+			outb(iobase + lpt_control, control & ~LPC_IENABLE);
+			outb(iobase + lpt_data, 0);
+			outb(iobase + lpt_control, control |= LPC_IENABLE);
+		}
+		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
+			outb(iobase + lpt_control, control & ~LPC_IENABLE);
+			outb(iobase + lpt_data, 0);
+			outb(iobase + lpt_control, control |= LPC_IENABLE);
+
+			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;
+	}
+	sc->sc_control = control;
+	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(int iobase, u_char *buf, int len)
+{
+	int num = len;
+	int i;
+	u_char cksum = 0, c;
+
+	while (len--) {
+		i = PLIPMXSPIN2;
+		while ((inb(iobase + lpt_status) & LPS_NBSY) != 0)
+			if (i-- < 0) return -1;
+		c = (inb(iobase + lpt_status) >> 3) & 0x0f;
+		outb(iobase + lpt_data, 0x11);
+		while ((inb(iobase + lpt_status) & LPS_NBSY) == 0)
+			if (i-- < 0) return -1;
+		c |= (inb(iobase + lpt_status) << 1) & 0xf0;
+		outb(iobase + lpt_data, 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;
+	int iobase = sc->sc_iobase;
+	struct mbuf *m;
+	struct ether_header *eh;
+	u_char *p = sc->sc_ifbuf, minibuf[4];
+	int c, i = 0, s, len, cksum;
+
+	if (!(inb(iobase + lpt_status) & LPS_NACK)) {
+		outb(iobase + lpt_control, sc->sc_control |= LPC_IENABLE);
+		ifp->if_collisions++;
+		return;
+	}
+	outb(iobase + lpt_data, 0x01);
+	outb(iobase + lpt_control, sc->sc_control &= ~LPC_IENABLE);
+
+#if defined(COMPAT_PLIP10)
+	if (ifp->if_flags & IFF_LINK0) {
+		if (plipreceive(iobase, 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(iobase, p, 1)) < 0) goto err;
+			p += 6;
+			if ((c = plipreceive(iobase, 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(iobase, p, len)) < 0) goto err;
+			break;
+		default:
+			goto err;
+		}
+	} else
+#endif
+	{
+		if (plipreceive(iobase, 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(iobase, p, len)) < 0) goto err;
+	}
+	
+	if (plipreceive(iobase, minibuf, 1) < 0) goto err;
+	if (cksum != minibuf[0]) {
+		log(LOG_NOTICE, "plip%d: checksum error\n", ifp->if_unit);
+		goto err;
+	}
+	outb(iobase + lpt_data, 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++;
+	outb(iobase + lpt_control, sc->sc_control |= LPC_IENABLE);
+	return;
+
+err:
+	outb(iobase + lpt_data, 0x00);
+
+	if (sc->sc_ifierrs < PLIPMXERRS) {
+		if (sc->sc_ifierrs > 4 && (inb(iobase + lpt_status) & LPS_NBSY)) {
+			/* Avoid interrupt nesting ... */
+			sc->sc_ifierrs = PLIPMXERRS - 1;
+		}
+		outb(iobase + lpt_control, sc->sc_control);
+	} 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);
+	}
+	ifp->if_ierrors++;
+	sc->sc_ifierrs++;
+	return;
+}
+
+static int
+pliptransmit(int iobase, u_char *buf, int len)
+{
+	int num = len;
+	int i;
+	u_char cksum = 0, c;
+
+	while (len--) {
+		i = PLIPMXSPIN2;
+		cksum += (c = *buf++);
+		while ((inb(iobase + lpt_status) & LPS_NBSY) == 0)
+			if (i-- < 0) return -1;
+		outb(iobase + lpt_data, c & 0x0f);
+		outb(iobase + lpt_data, c & 0x0f | 0x10);
+		c >>= 4;
+		while ((inb(iobase + lpt_status) & LPS_NBSY) != 0)
+			if (i-- < 0) return -1;
+		outb(iobase + lpt_data, c | 0x10);
+		outb(iobase + lpt_data, 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;
+	int iobase = sc->sc_iobase;
+	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) {
+				outb(iobase + lpt_data, 0x00);
+				sc->sc_pending = 0;
+				plipinput(sc);
+				i = PLIPMXSPIN1;
+			} else if (i-- < 0)
+				goto retry;
+			/* Retrigger remote interrupt */
+			outb(iobase + lpt_data, 0x08);
+		} while ((i8255->port_c & LPC_NERROR) == 0);
+		outb(iobase + lpt_control, sc->sc_control &= ~LPC_IENABLE);
+
+		if (pliptransmit(iobase, minibuf + 1, minibuf[0]) < 0) goto retry;
+		for (cksum = 0, m = m0; m; m = m->m_next) {
+			i = pliptransmit(iobase, mtod(m, u_char *), m->m_len);
+			if (i < 0) goto retry;
+			cksum += i;
+		}
+		if (pliptransmit(iobase, &cksum, 1) < 0) goto retry;
+		i = PLIPMXSPIN2;
+		while ((inb(iobase + lpt_status) & LPS_NBSY) == 0)
+			if (i-- < 0) goto retry;
+		outb(iobase + lpt_data, 0x00);
+
+		ifp->if_opackets++;
+		ifp->if_obytes += len + 4;
+		sc->sc_ifoerrs = 0;
+		s = splimp();
+		m_freem(m0);
+		splx(s);
+		outb(iobase + lpt_control, sc->sc_control |= LPC_IENABLE);
+	}
+	ifp->if_flags &= ~IFF_OACTIVE;
+	return;
+
+retry:
+	if (inb(iobase + lpt_status) & LPS_NACK)
+		ifp->if_collisions++;
+	else
+		ifp->if_oerrors++;
+
+	ifp->if_flags &= ~IFF_OACTIVE;
+	outb(iobase + lpt_data, 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);
+		outb(iobase + lpt_control, sc->sc_control |= LPC_IENABLE);
+		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);
+	}
+	sc->sc_ifoerrs++;
+}
+
+#endif

>Audit-Trail:
>Unformatted: