Subject: port-i386/1278: i386 has no PLIP interface
To: None <gnats-bugs@gnats.netbsd.org>
From: None <martin@rumolt.teuto.de>
List: netbsd-bugs
Date: 07/25/1995 22:46:25
>Number:         1278
>Category:       port-i386
>Synopsis:       add a PLIP interface to i386 parallel driver
>Confidential:   no
>Severity:       non-critical
>Priority:       high
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Jul 26 00:50:04 1995
>Last-Modified:
>Originator:     Martin Husemann
>Organization:
private
>Release:        sup from Jul 20
>Environment:
System: NetBSD rumolt.teuto.de 1.0A NetBSD 1.0A (RUMOLT) #4: Tue Jul 25 21:58:06 MET DST 1995 root@rumolt.teuto.de:/usr/src/sys/arch/i386/compile/RUMOLT i386


>Description:

While the pc532 port provides PLIP in its lpt.c driver (which was developed
from the i386 lpt.c driver), the i386 doesn't. (Just in case you don't
know: PLIP is the parallel IP protocol, used originaly by Russel Nelson's 
Crynwr packet driver for DOS. It emulates ethernet using a LapLink(tm)
cable connecting two parallel ports.)


>How-To-Repeat:

ifconfig -a
and watch the missing "plip0" line.

>Fix:

Reintegrate the PLIP parts of the pc532's lpt.c into sys/dev/isa/lpt.c.
The following patch does this. It works for me. I didn't stress-test it
yet. I didn't test it with PLIP_COMPAT10 defined.

To use it define options INET and PLIP in your kernel config file. Make
sure you have at least one ethernet driver configured to pull in the
generic ethernet support. (How do you tell "config" to require "ether"
on lpt.c if INET and PLIP are defined?)


*** lpt.c.orig	Sun Jul 23 10:36:56 1995
--- lpt.c	Tue Jul 25 21:57:38 1995
***************
*** 1,8 ****
--- 1,10 ----
  /*	$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.
   *
   * Redistribution and use in source and binary forms, with or without
***************
*** 62,71 ****
--- 64,87 ----
  #include <sys/ioctl.h>
  #include <sys/uio.h>
  #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>
  
  #include <dev/isa/isavar.h>
  #include <dev/isa/lptreg.h>
***************
*** 74,83 ****
--- 90,127 ----
  #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
+ 
  #if !defined(DEBUG) || !defined(notdef)
  #define lprintf
  #else
  #define lprintf		if (lptdebug) printf
  int lptdebug = 1;
***************
*** 101,116 ****
--- 145,178 ----
  #define	LPT_AUTOLF	0x20	/* automatic LF on CR */
  #define	LPT_NOPRIME	0x40	/* don't prime on open */
  #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 */
+ #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 plipstart(struct ifnet *);
+ static void plipintr(struct lpt_softc *);
+ #endif
+ 
  struct cfdriver lptcd = {
  	NULL, "lpt", lptprobe, lptattach, DV_TTY, sizeof(struct lpt_softc)
  };
  
  #define	LPTUNIT(s)	(minor(s) & 0x1f)
***************
*** 235,247 ****
--- 297,315 ----
  	sc->sc_iobase = iobase;
  	sc->sc_irq = ia->ia_irq;
  	sc->sc_state = 0;
  	outb(iobase + lpt_control, LPC_NINIT);
  
+ #if defined(INET) && defined(PLIP)
+ 	plipattach(sc, self->dv_unit);
+ 	sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE,
+ 				       ISA_IPL_NET, lptintr, sc);
+ #else
  	if (ia->ia_irq != IRQUNK)
  		sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE,
  		    ISA_IPL_NONE, lptintr, sc);
+ #endif
  }
  
  /*
   * Reset the printer, then wait until it's selected and not busy.
   */
***************
*** 483,492 ****
--- 551,567 ----
  	void *arg;
  {
  	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) {
+ 		plipintr(sc);
+ 		return;
+ 	}
+ #endif
+ 
  #if 0
  	if ((sc->sc_state & LPT_OPEN) == 0)
  		return 0;
  #endif
  
***************
*** 527,531 ****
--- 602,1012 ----
  		error = ENODEV;
  	}
  
  	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;
+ 
+ 	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_ifbuf)
+ 				sc->sc_ifbuf =
+ 					malloc(ifp->if_mtu + ifp->if_hdrlen,
+ 					       M_DEVBUF, M_WAITOK);
+ 		        ifp->if_flags |= IFF_RUNNING;
+ 			control &= ~LPC_IENABLE;
+ 			outb(iobase + lpt_control, control);
+ 			outb(iobase + lpt_data, 0);
+ 			control |= LPC_IENABLE;
+ 			outb(iobase + lpt_control, control);
+ 		}
+ 		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
+ 			control &= ~LPC_IENABLE;
+ 			outb(iobase + lpt_control, control);
+ 			outb(iobase + lpt_data, 0);
+ 			control |= LPC_IENABLE;
+ 			outb(iobase + lpt_control, control);
+ 
+ 			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 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
+ plipintr(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;
+ 
+ 	outb(iobase + lpt_control, sc->sc_control & ~LPC_IENABLE);
+ 	outb(iobase + lpt_data, 0x01);
+ 
+ #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);
+ 	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];
+ 	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 *))plipstart, ifp);
+ 
+ 	for (;;) {
+ 		s = splimp();
+ 		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;
+ 		}
+ 
+ 		for (i = PLIPMXSPIN1; (inb(iobase + lpt_status) & LPS_NERR) != 0; i--)
+ 			if (i < 0) goto retry;
+ 
+ 		/* Trigger remote interrupt */
+ 		sc->sc_control &= ~LPC_IENABLE;
+ 		outb(iobase + lpt_control, sc->sc_control);
+ 		outb(iobase + lpt_data, 0x08);
+ 
+ 		for (i = PLIPMXSPIN1; (inb(iobase + lpt_status) & LPS_NERR) == 0; i--)
+ 			if (i < 0 || (i > PLIPMXSPIN1/3
+ 			    && inb(iobase + lpt_status) & LPS_NACK)) 
+ 				goto retry;
+ 
+ 		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);
+ 		sc->sc_control |= LPC_IENABLE;
+ 		outb(iobase + lpt_control, sc->sc_control);
+ 	}
+ 	ifp->if_flags &= ~IFF_OACTIVE;
+ 	return;
+ 
+ retry:
+ 	if (inb(iobase + lpt_status) & LPS_NACK)
+ 		ifp->if_collisions++;
+ 	else
+ 		ifp->if_oerrors++;
+ 	if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == (IFF_RUNNING | IFF_UP)
+ 	    && sc->sc_ifoerrs < PLIPMXRETRY) {
+ 		s = splimp();
+ 		IF_PREPEND(&ifp->if_snd, m0);
+ 		splx(s);
+ 		timeout((void (*)(void *))plipstart, ifp, 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++;
+ 	ifp->if_flags &= ~IFF_OACTIVE;
+ 	outb(iobase + lpt_data, 0x00);
+ 	if (sc->sc_ifierrs >= PLIPMXERRS)
+ 	       ;
+ 	else
+ 	{
+ 		sc->sc_control |= LPC_IENABLE;
+ 		outb(iobase + lpt_control, sc->sc_control);
+ 	}
+ 	return;
+ }
+ 
+ #endif
>Audit-Trail:
>Unformatted: