if_plip.c revision 42443
138061Smsmith/*-
238061Smsmith * Copyright (c) 1997 Poul-Henning Kamp
338061Smsmith * All rights reserved.
438061Smsmith *
538061Smsmith * Redistribution and use in source and binary forms, with or without
638061Smsmith * modification, are permitted provided that the following conditions
738061Smsmith * are met:
838061Smsmith * 1. Redistributions of source code must retain the above copyright
938061Smsmith *    notice, this list of conditions and the following disclaimer.
1038061Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138061Smsmith *    notice, this list of conditions and the following disclaimer in the
1238061Smsmith *    documentation and/or other materials provided with the distribution.
1338061Smsmith *
1438061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738061Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438061Smsmith * SUCH DAMAGE.
2538061Smsmith *
2638061Smsmith *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
2742443Snsouch *	$Id: if_plip.c,v 1.6 1998/11/07 14:35:41 nsouch Exp $
2838061Smsmith */
2938061Smsmith
3038061Smsmith/*
3138061Smsmith * Parallel port TCP/IP interfaces added.  I looked at the driver from
3238061Smsmith * MACH but this is a complete rewrite, and btw. incompatible, and it
3338061Smsmith * should perform better too.  I have never run the MACH driver though.
3438061Smsmith *
3538061Smsmith * This driver sends two bytes (0x08, 0x00) in front of each packet,
3638061Smsmith * to allow us to distinguish another format later.
3738061Smsmith *
3838061Smsmith * Now added an Linux/Crynwr compatibility mode which is enabled using
3938061Smsmith * IF_LINK0 - Tim Wilkinson.
4038061Smsmith *
4138061Smsmith * TODO:
4238061Smsmith *    Make HDLC/PPP mode, use IF_LLC1 to enable.
4338061Smsmith *
4438061Smsmith * Connect the two computers using a Laplink parallel cable to use this
4538061Smsmith * feature:
4638061Smsmith *
4738061Smsmith *      +----------------------------------------+
4838061Smsmith * 	|A-name	A-End	B-End	Descr.	Port/Bit |
4938061Smsmith *      +----------------------------------------+
5038061Smsmith *	|DATA0	2	15	Data	0/0x01   |
5138061Smsmith *	|-ERROR	15	2	   	1/0x08   |
5238061Smsmith *      +----------------------------------------+
5338061Smsmith *	|DATA1	3	13	Data	0/0x02	 |
5438061Smsmith *	|+SLCT	13	3	   	1/0x10   |
5538061Smsmith *      +----------------------------------------+
5638061Smsmith *	|DATA2	4	12	Data	0/0x04   |
5738061Smsmith *	|+PE	12	4	   	1/0x20   |
5838061Smsmith *      +----------------------------------------+
5938061Smsmith *	|DATA3	5	10	Strobe	0/0x08   |
6038061Smsmith *	|-ACK	10	5	   	1/0x40   |
6138061Smsmith *      +----------------------------------------+
6238061Smsmith *	|DATA4	6	11	Data	0/0x10   |
6338061Smsmith *	|BUSY	11	6	   	1/~0x80  |
6438061Smsmith *      +----------------------------------------+
6538061Smsmith *	|GND	18-25	18-25	GND	-        |
6638061Smsmith *      +----------------------------------------+
6738061Smsmith *
6838061Smsmith * Expect transfer-rates up to 75 kbyte/sec.
6938061Smsmith *
7038061Smsmith * If GCC could correctly grok
7138061Smsmith *	register int port asm("edx")
7238061Smsmith * the code would be cleaner
7338061Smsmith *
7438061Smsmith * Poul-Henning Kamp <phk@freebsd.org>
7538061Smsmith */
7638061Smsmith
7738061Smsmith/*
7838061Smsmith * Update for ppbus, PLIP support only - Nicolas Souchu
7938061Smsmith */
8038061Smsmith
8138061Smsmith#include <sys/param.h>
8238061Smsmith#include <sys/systm.h>
8338061Smsmith#include <sys/mbuf.h>
8438061Smsmith#include <sys/socket.h>
8538061Smsmith#include <sys/sockio.h>
8638061Smsmith#include <sys/kernel.h>
8738061Smsmith#include <sys/malloc.h>
8838061Smsmith
8938061Smsmith#include <net/if.h>
9038061Smsmith#include <net/if_types.h>
9138061Smsmith#include <net/netisr.h>
9238061Smsmith
9338061Smsmith#include <netinet/in.h>
9438061Smsmith#include <netinet/in_var.h>
9538061Smsmith
9638061Smsmith#include "bpfilter.h"
9738061Smsmith#if NBPFILTER > 0
9838061Smsmith#include <net/bpf.h>
9938061Smsmith#endif
10038061Smsmith
10138061Smsmith#include <dev/ppbus/ppbconf.h>
10238061Smsmith#include <dev/ppbus/nlpt.h>
10338061Smsmith
10438061Smsmith#ifndef LPMTU			/* MTU for the lp# interfaces */
10538061Smsmith#define	LPMTU	1500
10638061Smsmith#endif
10738061Smsmith
10838061Smsmith#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
10938061Smsmith#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
11038061Smsmith#endif
11138061Smsmith
11238061Smsmith#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
11338061Smsmith#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
11438061Smsmith#endif
11538061Smsmith
11638061Smsmith#ifndef LPMAXERRS		/* Max errors before !RUNNING */
11738061Smsmith#define	LPMAXERRS	100
11838061Smsmith#endif
11938061Smsmith
12038061Smsmith#define CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
12138061Smsmith#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
12238061Smsmith#define MLPIPHDRLEN	CLPIPHDRLEN
12338061Smsmith
12438061Smsmith#define LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
12538061Smsmith#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
12638061Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
12738061Smsmith#define MLPIPHDRLEN	LPIPHDRLEN
12838061Smsmith#endif
12938061Smsmith
13038061Smsmith#define	LPIPTBLSIZE	256	/* Size of octet translation table */
13138061Smsmith
13240993Snsouch#ifndef DEBUG
13338061Smsmith#define DEBUG
13440993Snsouch#endif
13538061Smsmith
13638061Smsmith#ifndef DEBUG
13738061Smsmith#define lprintf (void)
13838061Smsmith#else
13938061Smsmith#define lprintf		if (lptflag) printf
14038061Smsmithstatic int volatile lptflag = 1;
14138061Smsmith#endif
14238061Smsmith
14338061Smsmithstruct lpt_softc {
14438061Smsmith	unsigned short lp_unit;
14538061Smsmith
14638061Smsmith	struct ppb_device lp_dev;
14738061Smsmith
14838061Smsmith	struct  ifnet	sc_if;
14938061Smsmith	u_char		*sc_ifbuf;
15038061Smsmith	int		sc_iferrs;
15138061Smsmith};
15238061Smsmith
15338061Smsmithstatic int	nlp = 0;
15438061Smsmith#define MAXPLIP	8			/* XXX not much better! */
15538061Smsmithstatic struct lpt_softc *lpdata[MAXPLIP];
15638061Smsmith
15738061Smsmith
15838061Smsmith/* Tables for the lp# interface */
15938061Smsmithstatic u_char *txmith;
16038061Smsmith#define txmitl (txmith+(1*LPIPTBLSIZE))
16138061Smsmith#define trecvh (txmith+(2*LPIPTBLSIZE))
16238061Smsmith#define trecvl (txmith+(3*LPIPTBLSIZE))
16338061Smsmith
16438061Smsmithstatic u_char *ctxmith;
16538061Smsmith#define ctxmitl (ctxmith+(1*LPIPTBLSIZE))
16638061Smsmith#define ctrecvh (ctxmith+(2*LPIPTBLSIZE))
16738061Smsmith#define ctrecvl (ctxmith+(3*LPIPTBLSIZE))
16838061Smsmith
16938061Smsmith/* Functions for the lp# interface */
17038061Smsmithstatic struct ppb_device	*lpprobe(struct ppb_data *);
17138061Smsmithstatic int			lpattach(struct ppb_device *);
17238061Smsmith
17338061Smsmithstatic int lpinittables(void);
17438061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t);
17538061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
17638061Smsmith	struct rtentry *);
17738061Smsmithstatic void lpintr(int);
17838061Smsmith
17938061Smsmith/*
18038061Smsmith * Make ourselves visible as a ppbus driver
18138061Smsmith */
18238061Smsmith
18338061Smsmithstatic struct ppb_driver lpdriver = {
18438061Smsmith    lpprobe, lpattach, "lp"
18538061Smsmith};
18638061SmsmithDATA_SET(ppbdriver_set, lpdriver);
18738061Smsmith
18838061Smsmith
18938061Smsmith/*
19038061Smsmith * lpprobe()
19138061Smsmith */
19238061Smsmithstatic struct ppb_device *
19338061Smsmithlpprobe(struct ppb_data *ppb)
19438061Smsmith{
19538061Smsmith	struct lpt_softc *lp;
19638061Smsmith
19738061Smsmith	/* if we haven't interrupts, the probe fails */
19838061Smsmith	if (!ppb->ppb_link->id_irq)
19938061Smsmith		return (0);
20038061Smsmith
20138061Smsmith	lp = (struct lpt_softc *) malloc(sizeof(struct lpt_softc),
20238061Smsmith							M_TEMP, M_NOWAIT);
20338061Smsmith	if (!lp) {
20438061Smsmith		printf("lp: cannot malloc!\n");
20538061Smsmith		return (0);
20638061Smsmith	}
20738061Smsmith	bzero(lp, sizeof(struct lpt_softc));
20838061Smsmith
20938061Smsmith	lpdata[nlp] = lp;
21038061Smsmith
21138061Smsmith	/*
21238061Smsmith	 * lp dependent initialisation.
21338061Smsmith	 */
21438061Smsmith	lp->lp_unit = nlp;
21538061Smsmith
21638061Smsmith	if (bootverbose)
21740039Sdes		printf("plip: irq %d\n", ppb->ppb_link->id_irq);
21838061Smsmith
21938061Smsmith	/*
22038061Smsmith	 * ppbus dependent initialisation.
22138061Smsmith	 */
22238061Smsmith	lp->lp_dev.id_unit = lp->lp_unit;
22338061Smsmith	lp->lp_dev.name = lpdriver.name;
22438061Smsmith	lp->lp_dev.ppb = ppb;
22538061Smsmith	lp->lp_dev.intr = lpintr;
22638061Smsmith
22738061Smsmith	/* Ok, go to next device on next probe */
22838061Smsmith	nlp ++;
22938061Smsmith
23038061Smsmith	return (&lp->lp_dev);
23138061Smsmith}
23238061Smsmith
23338061Smsmithstatic int
23438061Smsmithlpattach (struct ppb_device *dev)
23538061Smsmith{
23638061Smsmith	int unit = dev->id_unit;
23738061Smsmith	struct lpt_softc *sc = lpdata[unit];
23838061Smsmith	struct ifnet *ifp = &sc->sc_if;
23938061Smsmith
24038061Smsmith	/*
24138061Smsmith	 * Report ourselves
24238061Smsmith	 */
24338061Smsmith	printf("plip%d: <PLIP network interface> on ppbus %d\n",
24438061Smsmith	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
24538061Smsmith
24638061Smsmith	ifp->if_softc = sc;
24738061Smsmith	ifp->if_name = "lp";
24838061Smsmith	ifp->if_unit = unit;
24938061Smsmith	ifp->if_mtu = LPMTU;
25038061Smsmith	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
25138061Smsmith	ifp->if_ioctl = lpioctl;
25238061Smsmith	ifp->if_output = lpoutput;
25338061Smsmith	ifp->if_type = IFT_PARA;
25438061Smsmith	ifp->if_hdrlen = 0;
25538061Smsmith	ifp->if_addrlen = 0;
25638061Smsmith	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
25738061Smsmith	if_attach(ifp);
25838061Smsmith
25938061Smsmith#if NBPFILTER > 0
26038061Smsmith	bpfattach(ifp, DLT_NULL, LPIPHDRLEN);
26138061Smsmith#endif
26238061Smsmith
26338061Smsmith	return (1);
26438061Smsmith}
26538061Smsmith/*
26638061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol.
26738061Smsmith * We don't want to calculate these nasties in our tight loop, so we
26838061Smsmith * precalculate them when we initialize.
26938061Smsmith */
27038061Smsmithstatic int
27138061Smsmithlpinittables (void)
27238061Smsmith{
27338061Smsmith    int i;
27438061Smsmith
27538061Smsmith    if (!txmith)
27638061Smsmith	txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
27738061Smsmith
27838061Smsmith    if (!txmith)
27938061Smsmith	return 1;
28038061Smsmith
28138061Smsmith    if (!ctxmith)
28238061Smsmith	ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
28338061Smsmith
28438061Smsmith    if (!ctxmith)
28538061Smsmith	return 1;
28638061Smsmith
28738061Smsmith    for (i=0; i < LPIPTBLSIZE; i++) {
28838061Smsmith	ctxmith[i] = (i & 0xF0) >> 4;
28938061Smsmith	ctxmitl[i] = 0x10 | (i & 0x0F);
29038061Smsmith	ctrecvh[i] = (i & 0x78) << 1;
29138061Smsmith	ctrecvl[i] = (i & 0x78) >> 3;
29238061Smsmith    }
29338061Smsmith
29438061Smsmith    for (i=0; i < LPIPTBLSIZE; i++) {
29538061Smsmith	txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
29638061Smsmith	txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
29738061Smsmith	trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
29838061Smsmith	trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
29938061Smsmith    }
30038061Smsmith
30138061Smsmith    return 0;
30238061Smsmith}
30338061Smsmith
30438061Smsmith/*
30538061Smsmith * Process an ioctl request.
30638061Smsmith */
30738061Smsmith
30838061Smsmithstatic int
30938061Smsmithlpioctl (struct ifnet *ifp, u_long cmd, caddr_t data)
31038061Smsmith{
31138061Smsmith    struct lpt_softc *sc = lpdata[ifp->if_unit];
31238061Smsmith    struct ifaddr *ifa = (struct ifaddr *)data;
31338061Smsmith    struct ifreq *ifr = (struct ifreq *)data;
31438061Smsmith    u_char *ptr;
31538061Smsmith    int error;
31638061Smsmith
31738061Smsmith    switch (cmd) {
31838061Smsmith
31938061Smsmith    case SIOCSIFDSTADDR:
32038061Smsmith    case SIOCAIFADDR:
32138061Smsmith    case SIOCSIFADDR:
32238061Smsmith	if (ifa->ifa_addr->sa_family != AF_INET)
32338061Smsmith	    return EAFNOSUPPORT;
32438061Smsmith
32538061Smsmith	ifp->if_flags |= IFF_UP;
32638061Smsmith	/* FALLTHROUGH */
32738061Smsmith    case SIOCSIFFLAGS:
32838061Smsmith	if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) {
32938061Smsmith
33038061Smsmith	    ppb_wctr(&sc->lp_dev, 0x00);
33138061Smsmith	    ifp->if_flags &= ~IFF_RUNNING;
33238061Smsmith
33338061Smsmith	    /* IFF_UP is not set, try to release the bus anyway */
33438061Smsmith	    ppb_release_bus(&sc->lp_dev);
33538061Smsmith	    break;
33638061Smsmith	}
33738061Smsmith	if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
33838061Smsmith
33942443Snsouch	    /* XXX
34038061Smsmith	     * Should the request be interruptible?
34138061Smsmith	     */
34238061Smsmith	    if ((error = ppb_request_bus(&sc->lp_dev, PPB_WAIT|PPB_INTR)))
34338061Smsmith		return (error);
34438061Smsmith
34542443Snsouch	    /* Now IFF_UP means that we own the bus */
34642443Snsouch
34742443Snsouch	    ppb_set_mode(&sc->lp_dev, PPB_COMPATIBLE);
34842443Snsouch
34938061Smsmith	    if (lpinittables()) {
35038061Smsmith		ppb_release_bus(&sc->lp_dev);
35138061Smsmith		return ENOBUFS;
35238061Smsmith	    }
35338061Smsmith
35438061Smsmith	    sc->sc_ifbuf = malloc(sc->sc_if.if_mtu + MLPIPHDRLEN,
35538061Smsmith				  M_DEVBUF, M_WAITOK);
35638061Smsmith	    if (!sc->sc_ifbuf) {
35738061Smsmith		ppb_release_bus(&sc->lp_dev);
35838061Smsmith		return ENOBUFS;
35938061Smsmith	    }
36038061Smsmith
36138061Smsmith	    ppb_wctr(&sc->lp_dev, LPC_ENA);
36238061Smsmith	    ifp->if_flags |= IFF_RUNNING;
36338061Smsmith	}
36438061Smsmith	break;
36538061Smsmith
36638061Smsmith    case SIOCSIFMTU:
36738061Smsmith	ptr = sc->sc_ifbuf;
36838061Smsmith	sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT);
36938061Smsmith	if (!sc->sc_ifbuf) {
37038061Smsmith	    sc->sc_ifbuf = ptr;
37138061Smsmith	    return ENOBUFS;
37238061Smsmith	}
37338061Smsmith	if (ptr)
37438061Smsmith	    free(ptr,M_DEVBUF);
37538061Smsmith	sc->sc_if.if_mtu = ifr->ifr_mtu;
37638061Smsmith	break;
37738061Smsmith
37838061Smsmith    case SIOCGIFMTU:
37938061Smsmith	ifr->ifr_mtu = sc->sc_if.if_mtu;
38038061Smsmith	break;
38138061Smsmith
38238061Smsmith    case SIOCADDMULTI:
38338061Smsmith    case SIOCDELMULTI:
38438061Smsmith	if (ifr == 0) {
38538061Smsmith	    return EAFNOSUPPORT;		/* XXX */
38638061Smsmith	}
38738061Smsmith	switch (ifr->ifr_addr.sa_family) {
38838061Smsmith
38938061Smsmith	case AF_INET:
39038061Smsmith	    break;
39138061Smsmith
39238061Smsmith	default:
39338061Smsmith	    return EAFNOSUPPORT;
39438061Smsmith	}
39538061Smsmith	break;
39638061Smsmith
39740626Smsmith    case SIOCGIFMEDIA:
39840626Smsmith	/*
39940626Smsmith	 * No ifmedia support at this stage; maybe use it
40040626Smsmith	 * in future for eg. protocol selection.
40140626Smsmith	 */
40240626Smsmith	return EINVAL;
40340626Smsmith
40438061Smsmith    default:
40538373Sbde	lprintf("LP:ioctl(0x%lx)\n", cmd);
40638061Smsmith	return EINVAL;
40738061Smsmith    }
40838061Smsmith    return 0;
40938061Smsmith}
41038061Smsmith
41138061Smsmithstatic __inline int
41238061Smsmithclpoutbyte (u_char byte, int spin, struct ppb_device *dev)
41338061Smsmith{
41438061Smsmith	ppb_wdtr(dev, ctxmitl[byte]);
41538061Smsmith	while (ppb_rstr(dev) & CLPIP_SHAKE)
41638061Smsmith		if (--spin == 0) {
41738061Smsmith			return 1;
41838061Smsmith		}
41938061Smsmith	ppb_wdtr(dev, ctxmith[byte]);
42038061Smsmith	while (!(ppb_rstr(dev) & CLPIP_SHAKE))
42138061Smsmith		if (--spin == 0) {
42238061Smsmith			return 1;
42338061Smsmith		}
42438061Smsmith	return 0;
42538061Smsmith}
42638061Smsmith
42738061Smsmithstatic __inline int
42838061Smsmithclpinbyte (int spin, struct ppb_device *dev)
42938061Smsmith{
43042443Snsouch	u_char c, cl;
43138061Smsmith
43238061Smsmith	while((ppb_rstr(dev) & CLPIP_SHAKE))
43338061Smsmith	    if(!--spin) {
43438061Smsmith		return -1;
43538061Smsmith	    }
43638061Smsmith	cl = ppb_rstr(dev);
43738061Smsmith	ppb_wdtr(dev, 0x10);
43838061Smsmith
43938061Smsmith	while(!(ppb_rstr(dev) & CLPIP_SHAKE))
44038061Smsmith	    if(!--spin) {
44138061Smsmith		return -1;
44238061Smsmith	    }
44338061Smsmith	c = ppb_rstr(dev);
44438061Smsmith	ppb_wdtr(dev, 0x00);
44538061Smsmith
44638061Smsmith	return (ctrecvl[cl] | ctrecvh[c]);
44738061Smsmith}
44838061Smsmith
44938061Smsmithstatic void
45038061Smsmithlpintr (int unit)
45138061Smsmith{
45238061Smsmith	struct   lpt_softc *sc = lpdata[unit];
45338061Smsmith	int len, s, j;
45438061Smsmith	u_char *bp;
45538061Smsmith	u_char c, cl;
45638061Smsmith	struct mbuf *top;
45738061Smsmith
45838061Smsmith	s = splhigh();
45938061Smsmith
46038061Smsmith	if (sc->sc_if.if_flags & IFF_LINK0) {
46138061Smsmith
46238061Smsmith	    /* Ack. the request */
46338061Smsmith	    ppb_wdtr(&sc->lp_dev, 0x01);
46438061Smsmith
46538061Smsmith	    /* Get the packet length */
46638061Smsmith	    j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
46738061Smsmith	    if (j == -1)
46838061Smsmith		goto err;
46938061Smsmith	    len = j;
47038061Smsmith	    j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
47138061Smsmith	    if (j == -1)
47238061Smsmith		goto err;
47338061Smsmith	    len = len + (j << 8);
47438061Smsmith	    if (len > sc->sc_if.if_mtu + MLPIPHDRLEN)
47538061Smsmith		goto err;
47638061Smsmith
47738061Smsmith	    bp  = sc->sc_ifbuf;
47838061Smsmith
47938061Smsmith	    while (len--) {
48038061Smsmith	        j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
48138061Smsmith	        if (j == -1) {
48238061Smsmith		    goto err;
48338061Smsmith	        }
48438061Smsmith	        *bp++ = j;
48538061Smsmith	    }
48638061Smsmith	    /* Get and ignore checksum */
48738061Smsmith	    j = clpinbyte(LPMAXSPIN2, &sc->lp_dev);
48838061Smsmith	    if (j == -1) {
48938061Smsmith	        goto err;
49038061Smsmith	    }
49138061Smsmith
49238061Smsmith	    len = bp - sc->sc_ifbuf;
49338061Smsmith	    if (len <= CLPIPHDRLEN)
49438061Smsmith	        goto err;
49538061Smsmith
49638061Smsmith	    sc->sc_iferrs = 0;
49738061Smsmith
49838061Smsmith	    if (IF_QFULL(&ipintrq)) {
49938061Smsmith	        lprintf("DROP");
50038061Smsmith	        IF_DROP(&ipintrq);
50138061Smsmith		goto done;
50238061Smsmith	    }
50338061Smsmith	    len -= CLPIPHDRLEN;
50438061Smsmith	    sc->sc_if.if_ipackets++;
50538061Smsmith	    sc->sc_if.if_ibytes += len;
50638061Smsmith	    top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, &sc->sc_if, 0);
50738061Smsmith	    if (top) {
50838061Smsmith	        IF_ENQUEUE(&ipintrq, top);
50938061Smsmith	        schednetisr(NETISR_IP);
51038061Smsmith	    }
51138061Smsmith	    goto done;
51238061Smsmith	}
51338061Smsmith	while ((ppb_rstr(&sc->lp_dev) & LPIP_SHAKE)) {
51438061Smsmith	    len = sc->sc_if.if_mtu + LPIPHDRLEN;
51538061Smsmith	    bp  = sc->sc_ifbuf;
51638061Smsmith	    while (len--) {
51738061Smsmith
51838061Smsmith		cl = ppb_rstr(&sc->lp_dev);
51938061Smsmith		ppb_wdtr(&sc->lp_dev, 8);
52038061Smsmith
52138061Smsmith		j = LPMAXSPIN2;
52238061Smsmith		while((ppb_rstr(&sc->lp_dev) & LPIP_SHAKE))
52338061Smsmith		    if(!--j) goto err;
52438061Smsmith
52538061Smsmith		c = ppb_rstr(&sc->lp_dev);
52638061Smsmith		ppb_wdtr(&sc->lp_dev, 0);
52738061Smsmith
52838061Smsmith		*bp++= trecvh[cl] | trecvl[c];
52938061Smsmith
53038061Smsmith		j = LPMAXSPIN2;
53138061Smsmith		while (!((cl=ppb_rstr(&sc->lp_dev)) & LPIP_SHAKE)) {
53238061Smsmith		    if (cl != c &&
53338061Smsmith			(((cl = ppb_rstr(&sc->lp_dev)) ^ 0xb8) & 0xf8) ==
53438061Smsmith			  (c & 0xf8))
53538061Smsmith			goto end;
53638061Smsmith		    if (!--j) goto err;
53738061Smsmith		}
53838061Smsmith	    }
53938061Smsmith
54038061Smsmith	end:
54138061Smsmith	    len = bp - sc->sc_ifbuf;
54238061Smsmith	    if (len <= LPIPHDRLEN)
54338061Smsmith		goto err;
54438061Smsmith
54538061Smsmith	    sc->sc_iferrs = 0;
54638061Smsmith
54738061Smsmith	    if (IF_QFULL(&ipintrq)) {
54838061Smsmith		lprintf("DROP");
54938061Smsmith		IF_DROP(&ipintrq);
55038061Smsmith		goto done;
55138061Smsmith	    }
55238061Smsmith#if NBPFILTER > 0
55338061Smsmith	    if (sc->sc_if.if_bpf) {
55438061Smsmith		bpf_tap(&sc->sc_if, sc->sc_ifbuf, len);
55538061Smsmith	    }
55638061Smsmith#endif
55738061Smsmith	    len -= LPIPHDRLEN;
55838061Smsmith	    sc->sc_if.if_ipackets++;
55938061Smsmith	    sc->sc_if.if_ibytes += len;
56038061Smsmith	    top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, &sc->sc_if, 0);
56138061Smsmith	    if (top) {
56238061Smsmith		    IF_ENQUEUE(&ipintrq, top);
56338061Smsmith		    schednetisr(NETISR_IP);
56438061Smsmith	    }
56538061Smsmith	}
56638061Smsmith	goto done;
56738061Smsmith
56838061Smsmith    err:
56938061Smsmith	ppb_wdtr(&sc->lp_dev, 0);
57038061Smsmith	lprintf("R");
57138061Smsmith	sc->sc_if.if_ierrors++;
57238061Smsmith	sc->sc_iferrs++;
57338061Smsmith
57438061Smsmith	/*
57538061Smsmith	 * We are not able to send receive anything for now,
57638061Smsmith	 * so stop wasting our time
57738061Smsmith	 */
57838061Smsmith	if (sc->sc_iferrs > LPMAXERRS) {
57938061Smsmith	    printf("lp%d: Too many errors, Going off-line.\n", unit);
58038061Smsmith	    ppb_wctr(&sc->lp_dev, 0x00);
58138061Smsmith	    sc->sc_if.if_flags &= ~IFF_RUNNING;
58238061Smsmith	    sc->sc_iferrs=0;
58338061Smsmith	}
58438061Smsmith
58538061Smsmith    done:
58638061Smsmith	splx(s);
58738061Smsmith	return;
58838061Smsmith}
58938061Smsmith
59038061Smsmithstatic __inline int
59138061Smsmithlpoutbyte (u_char byte, int spin, struct ppb_device *dev)
59238061Smsmith{
59338061Smsmith    ppb_wdtr(dev, txmith[byte]);
59438061Smsmith    while (!(ppb_rstr(dev) & LPIP_SHAKE))
59538061Smsmith	if (--spin == 0)
59638061Smsmith		return 1;
59738061Smsmith    ppb_wdtr(dev, txmitl[byte]);
59838061Smsmith    while (ppb_rstr(dev) & LPIP_SHAKE)
59938061Smsmith	if (--spin == 0)
60038061Smsmith		return 1;
60138061Smsmith    return 0;
60238061Smsmith}
60338061Smsmith
60438061Smsmithstatic int
60538061Smsmithlpoutput (struct ifnet *ifp, struct mbuf *m,
60638061Smsmith	  struct sockaddr *dst, struct rtentry *rt)
60738061Smsmith{
60838061Smsmith    struct lpt_softc *sc = lpdata[ifp->if_unit];
60938061Smsmith    int s, err;
61038061Smsmith    struct mbuf *mm;
61138061Smsmith    u_char *cp = "\0\0";
61238061Smsmith    u_char chksum = 0;
61338061Smsmith    int count = 0;
61438061Smsmith    int i;
61538061Smsmith    int spin;
61638061Smsmith
61738061Smsmith    /* We need a sensible value if we abort */
61838061Smsmith    cp++;
61938061Smsmith    ifp->if_flags |= IFF_RUNNING;
62038061Smsmith
62138061Smsmith    err = 1;			/* assume we're aborting because of an error */
62238061Smsmith
62338061Smsmith    s = splhigh();
62438061Smsmith
62538061Smsmith    /* Suspend (on laptops) or receive-errors might have taken us offline */
62638061Smsmith    ppb_wctr(&sc->lp_dev, LPC_ENA);
62738061Smsmith
62838061Smsmith    if (ifp->if_flags & IFF_LINK0) {
62938061Smsmith
63038061Smsmith	if (!(ppb_rstr(&sc->lp_dev) & CLPIP_SHAKE)) {
63138061Smsmith	    lprintf("&");
63238061Smsmith	    lpintr(ifp->if_unit);
63338061Smsmith	}
63438061Smsmith
63538061Smsmith	/* Alert other end to pending packet */
63638061Smsmith	spin = LPMAXSPIN1;
63738061Smsmith	ppb_wdtr(&sc->lp_dev, 0x08);
63838061Smsmith	while ((ppb_rstr(&sc->lp_dev) & 0x08) == 0)
63938061Smsmith		if (--spin == 0) {
64038061Smsmith			goto nend;
64138061Smsmith		}
64238061Smsmith
64338061Smsmith	/* Calculate length of packet, then send that */
64438061Smsmith
64538061Smsmith	count += 14;		/* Ethernet header len */
64638061Smsmith
64738061Smsmith	mm = m;
64838061Smsmith	for (mm = m; mm; mm = mm->m_next) {
64938061Smsmith		count += mm->m_len;
65038061Smsmith	}
65138061Smsmith	if (clpoutbyte(count & 0xFF, LPMAXSPIN1, &sc->lp_dev))
65238061Smsmith		goto nend;
65338061Smsmith	if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, &sc->lp_dev))
65438061Smsmith		goto nend;
65538061Smsmith
65638061Smsmith	/* Send dummy ethernet header */
65738061Smsmith	for (i = 0; i < 12; i++) {
65838061Smsmith		if (clpoutbyte(i, LPMAXSPIN1, &sc->lp_dev))
65938061Smsmith			goto nend;
66038061Smsmith		chksum += i;
66138061Smsmith	}
66238061Smsmith
66338061Smsmith	if (clpoutbyte(0x08, LPMAXSPIN1, &sc->lp_dev))
66438061Smsmith		goto nend;
66538061Smsmith	if (clpoutbyte(0x00, LPMAXSPIN1, &sc->lp_dev))
66638061Smsmith		goto nend;
66738061Smsmith	chksum += 0x08 + 0x00;		/* Add into checksum */
66838061Smsmith
66938061Smsmith	mm = m;
67038061Smsmith	do {
67138061Smsmith		cp = mtod(mm, u_char *);
67238061Smsmith		while (mm->m_len--) {
67338061Smsmith			chksum += *cp;
67438061Smsmith			if (clpoutbyte(*cp++, LPMAXSPIN2, &sc->lp_dev))
67538061Smsmith				goto nend;
67638061Smsmith		}
67738061Smsmith	} while ((mm = mm->m_next));
67838061Smsmith
67938061Smsmith	/* Send checksum */
68038061Smsmith	if (clpoutbyte(chksum, LPMAXSPIN2, &sc->lp_dev))
68138061Smsmith		goto nend;
68238061Smsmith
68338061Smsmith	/* Go quiescent */
68438061Smsmith	ppb_wdtr(&sc->lp_dev, 0);
68538061Smsmith
68638061Smsmith	err = 0;			/* No errors */
68738061Smsmith
68838061Smsmith	nend:
68938061Smsmith	if (err)  {				/* if we didn't timeout... */
69038061Smsmith		ifp->if_oerrors++;
69138061Smsmith		lprintf("X");
69238061Smsmith	} else {
69338061Smsmith		ifp->if_opackets++;
69438061Smsmith		ifp->if_obytes += m->m_pkthdr.len;
69538061Smsmith	}
69638061Smsmith
69738061Smsmith	m_freem(m);
69838061Smsmith
69938061Smsmith	if (!(ppb_rstr(&sc->lp_dev) & CLPIP_SHAKE)) {
70038061Smsmith		lprintf("^");
70138061Smsmith		lpintr(ifp->if_unit);
70238061Smsmith	}
70338061Smsmith	(void) splx(s);
70438061Smsmith	return 0;
70538061Smsmith    }
70638061Smsmith
70738061Smsmith    if (ppb_rstr(&sc->lp_dev) & LPIP_SHAKE) {
70838061Smsmith        lprintf("&");
70938061Smsmith        lpintr(ifp->if_unit);
71038061Smsmith    }
71138061Smsmith
71238061Smsmith    if (lpoutbyte(0x08, LPMAXSPIN1, &sc->lp_dev))
71338061Smsmith        goto end;
71438061Smsmith    if (lpoutbyte(0x00, LPMAXSPIN2, &sc->lp_dev))
71538061Smsmith        goto end;
71638061Smsmith
71738061Smsmith    mm = m;
71838061Smsmith    do {
71938061Smsmith        cp = mtod(mm,u_char *);
72038061Smsmith        while (mm->m_len--)
72138061Smsmith	    if (lpoutbyte(*cp++, LPMAXSPIN2, &sc->lp_dev))
72238061Smsmith	        goto end;
72338061Smsmith    } while ((mm = mm->m_next));
72438061Smsmith
72538061Smsmith    err = 0;				/* no errors were encountered */
72638061Smsmith
72738061Smsmith    end:
72838061Smsmith    --cp;
72938061Smsmith    ppb_wdtr(&sc->lp_dev, txmitl[*cp] ^ 0x17);
73038061Smsmith
73138061Smsmith    if (err)  {				/* if we didn't timeout... */
73238061Smsmith	ifp->if_oerrors++;
73338061Smsmith        lprintf("X");
73438061Smsmith    } else {
73538061Smsmith	ifp->if_opackets++;
73638061Smsmith	ifp->if_obytes += m->m_pkthdr.len;
73738061Smsmith#if NBPFILTER > 0
73838061Smsmith	if (ifp->if_bpf) {
73938061Smsmith	    /*
74038061Smsmith	     * We need to prepend the packet type as
74138061Smsmith	     * a two byte field.  Cons up a dummy header
74238061Smsmith	     * to pacify bpf.  This is safe because bpf
74338061Smsmith	     * will only read from the mbuf (i.e., it won't
74438061Smsmith	     * try to free it or keep a pointer to it).
74538061Smsmith	     */
74638061Smsmith	    struct mbuf m0;
74738061Smsmith	    u_short hdr = 0x800;
74838061Smsmith
74938061Smsmith	    m0.m_next = m;
75038061Smsmith	    m0.m_len = 2;
75138061Smsmith	    m0.m_data = (char *)&hdr;
75238061Smsmith
75338061Smsmith	    bpf_mtap(ifp, &m0);
75438061Smsmith	}
75538061Smsmith#endif
75638061Smsmith    }
75738061Smsmith
75838061Smsmith    m_freem(m);
75938061Smsmith
76038061Smsmith    if (ppb_rstr(&sc->lp_dev) & LPIP_SHAKE) {
76138061Smsmith	lprintf("^");
76238061Smsmith	lpintr(ifp->if_unit);
76338061Smsmith    }
76438061Smsmith
76538061Smsmith    (void) splx(s);
76638061Smsmith    return 0;
76738061Smsmith}
768