if_plip.c revision 183053
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
2738061Smsmith */
2838061Smsmith
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ppbus/if_plip.c 183053 2008-09-15 22:26:32Z jhb $");
31119418Sobrien
3238061Smsmith/*
3338061Smsmith * Parallel port TCP/IP interfaces added.  I looked at the driver from
3438061Smsmith * MACH but this is a complete rewrite, and btw. incompatible, and it
3538061Smsmith * should perform better too.  I have never run the MACH driver though.
3638061Smsmith *
3738061Smsmith * This driver sends two bytes (0x08, 0x00) in front of each packet,
3838061Smsmith * to allow us to distinguish another format later.
3938061Smsmith *
40108470Sschweikh * Now added a Linux/Crynwr compatibility mode which is enabled using
4138061Smsmith * IF_LINK0 - Tim Wilkinson.
4238061Smsmith *
4338061Smsmith * TODO:
4438061Smsmith *    Make HDLC/PPP mode, use IF_LLC1 to enable.
4538061Smsmith *
4638061Smsmith * Connect the two computers using a Laplink parallel cable to use this
4738061Smsmith * feature:
4838061Smsmith *
4938061Smsmith *      +----------------------------------------+
5038061Smsmith * 	|A-name	A-End	B-End	Descr.	Port/Bit |
5138061Smsmith *      +----------------------------------------+
5238061Smsmith *	|DATA0	2	15	Data	0/0x01   |
5338061Smsmith *	|-ERROR	15	2	   	1/0x08   |
5438061Smsmith *      +----------------------------------------+
5538061Smsmith *	|DATA1	3	13	Data	0/0x02	 |
5638061Smsmith *	|+SLCT	13	3	   	1/0x10   |
5738061Smsmith *      +----------------------------------------+
5838061Smsmith *	|DATA2	4	12	Data	0/0x04   |
5938061Smsmith *	|+PE	12	4	   	1/0x20   |
6038061Smsmith *      +----------------------------------------+
6138061Smsmith *	|DATA3	5	10	Strobe	0/0x08   |
6238061Smsmith *	|-ACK	10	5	   	1/0x40   |
6338061Smsmith *      +----------------------------------------+
6438061Smsmith *	|DATA4	6	11	Data	0/0x10   |
6538061Smsmith *	|BUSY	11	6	   	1/~0x80  |
6638061Smsmith *      +----------------------------------------+
6738061Smsmith *	|GND	18-25	18-25	GND	-        |
6838061Smsmith *      +----------------------------------------+
6938061Smsmith *
7038061Smsmith * Expect transfer-rates up to 75 kbyte/sec.
7138061Smsmith *
7238061Smsmith * If GCC could correctly grok
7338061Smsmith *	register int port asm("edx")
7438061Smsmith * the code would be cleaner
7538061Smsmith *
7638061Smsmith * Poul-Henning Kamp <phk@freebsd.org>
7738061Smsmith */
7838061Smsmith
7938061Smsmith/*
8038061Smsmith * Update for ppbus, PLIP support only - Nicolas Souchu
8138061Smsmith */
8238061Smsmith
8355939Snsouch#include "opt_plip.h"
8455939Snsouch
8538061Smsmith#include <sys/param.h>
8638061Smsmith#include <sys/systm.h>
8755939Snsouch#include <sys/module.h>
8855939Snsouch#include <sys/bus.h>
8938061Smsmith#include <sys/mbuf.h>
9038061Smsmith#include <sys/socket.h>
9138061Smsmith#include <sys/sockio.h>
9238061Smsmith#include <sys/kernel.h>
9338061Smsmith#include <sys/malloc.h>
9438061Smsmith
9555939Snsouch#include <machine/bus.h>
9655939Snsouch#include <machine/resource.h>
9755939Snsouch#include <sys/rman.h>
9855939Snsouch
9938061Smsmith#include <net/if.h>
10038061Smsmith#include <net/if_types.h>
10138061Smsmith#include <net/netisr.h>
10238061Smsmith
10338061Smsmith#include <netinet/in.h>
10438061Smsmith#include <netinet/in_var.h>
10538061Smsmith
10638061Smsmith#include <net/bpf.h>
10738061Smsmith
10838061Smsmith#include <dev/ppbus/ppbconf.h>
10955939Snsouch#include "ppbus_if.h"
11055939Snsouch#include <dev/ppbus/ppbio.h>
11138061Smsmith
11238061Smsmith#ifndef LPMTU			/* MTU for the lp# interfaces */
11338061Smsmith#define	LPMTU	1500
11438061Smsmith#endif
11538061Smsmith
11638061Smsmith#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
11738061Smsmith#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
11838061Smsmith#endif
11938061Smsmith
12038061Smsmith#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
12138061Smsmith#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
12238061Smsmith#endif
12338061Smsmith
12438061Smsmith#ifndef LPMAXERRS		/* Max errors before !RUNNING */
12538061Smsmith#define	LPMAXERRS	100
12638061Smsmith#endif
12738061Smsmith
12838061Smsmith#define CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
12938061Smsmith#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
13038061Smsmith#define MLPIPHDRLEN	CLPIPHDRLEN
13138061Smsmith
13238061Smsmith#define LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
13338061Smsmith#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
13438061Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
13538061Smsmith#define MLPIPHDRLEN	LPIPHDRLEN
13638061Smsmith#endif
13738061Smsmith
13838061Smsmith#define	LPIPTBLSIZE	256	/* Size of octet translation table */
13938061Smsmith
14038061Smsmith#define lprintf		if (lptflag) printf
14143433Snsouch
14243433Snsouch#ifdef PLIP_DEBUG
14338061Smsmithstatic int volatile lptflag = 1;
14443433Snsouch#else
14543433Snsouchstatic int volatile lptflag = 0;
14638061Smsmith#endif
14738061Smsmith
14855939Snsouchstruct lp_data {
149147256Sbrooks	struct  ifnet	*sc_ifp;
15038061Smsmith	u_char		*sc_ifbuf;
15138061Smsmith	int		sc_iferrs;
15255939Snsouch
15355939Snsouch	struct resource *res_irq;
15438061Smsmith};
15538061Smsmith
15638061Smsmith/* Tables for the lp# interface */
15738061Smsmithstatic u_char *txmith;
15838061Smsmith#define txmitl (txmith+(1*LPIPTBLSIZE))
15938061Smsmith#define trecvh (txmith+(2*LPIPTBLSIZE))
16038061Smsmith#define trecvl (txmith+(3*LPIPTBLSIZE))
16138061Smsmith
16238061Smsmithstatic u_char *ctxmith;
16338061Smsmith#define ctxmitl (ctxmith+(1*LPIPTBLSIZE))
16438061Smsmith#define ctrecvh (ctxmith+(2*LPIPTBLSIZE))
16538061Smsmith#define ctrecvl (ctxmith+(3*LPIPTBLSIZE))
16638061Smsmith
16738061Smsmith/* Functions for the lp# interface */
16838061Smsmithstatic int lpinittables(void);
16938061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t);
17038061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
17138061Smsmith	struct rtentry *);
17255939Snsouchstatic void lp_intr(void *);
17338061Smsmith
17455939Snsouch#define DEVTOSOFTC(dev) \
17555939Snsouch	((struct lp_data *)device_get_softc(dev))
17655939Snsouch#define UNITODEVICE(unit) \
17755939Snsouch	(devclass_get_device(lp_devclass, (unit)))
17838061Smsmith
17955939Snsouchstatic devclass_t lp_devclass;
18055939Snsouch
18156455Speterstatic void
18256455Speterlp_identify(driver_t *driver, device_t parent)
18356455Speter{
184127189Sguido	device_t dev;
18555939Snsouch
186155606Sjhb	dev = device_find_child(parent, "plip", -1);
187127189Sguido	if (!dev)
188127189Sguido		BUS_ADD_CHILD(parent, 0, "plip", -1);
18956455Speter}
19038061Smsmith/*
19138061Smsmith * lpprobe()
19238061Smsmith */
19355939Snsouchstatic int
19455939Snsouchlp_probe(device_t dev)
19538061Smsmith{
19638061Smsmith
19755939Snsouch	device_set_desc(dev, "PLIP network interface");
19838061Smsmith
19955939Snsouch	return (0);
20038061Smsmith}
20138061Smsmith
20238061Smsmithstatic int
20355939Snsouchlp_attach (device_t dev)
20438061Smsmith{
20555939Snsouch	struct lp_data *lp = DEVTOSOFTC(dev);
206147256Sbrooks	struct ifnet *ifp;
207183053Sjhb	int rid = 0;
20838061Smsmith
209183053Sjhb	/*
210183053Sjhb	 * Reserve the interrupt resource.  If we don't have one, the
211183053Sjhb	 * attach fails.
212183053Sjhb	 */
213183053Sjhb	lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
214183053Sjhb	    RF_SHAREABLE);
215183053Sjhb	if (lp->res_irq == 0) {
216183053Sjhb		device_printf(dev, "cannot reserve interrupt, failed.\n");
217183053Sjhb		return (ENXIO);
218183053Sjhb	}
219183053Sjhb
220147256Sbrooks	ifp = lp->sc_ifp = if_alloc(IFT_PARA);
221147256Sbrooks	if (ifp == NULL) {
222147256Sbrooks		return (ENOSPC);
223147256Sbrooks	}
224147256Sbrooks
22555939Snsouch	ifp->if_softc = lp;
226121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
22738061Smsmith	ifp->if_mtu = LPMTU;
228133695Srwatson	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST |
229133695Srwatson	    IFF_NEEDSGIANT;
23038061Smsmith	ifp->if_ioctl = lpioctl;
23138061Smsmith	ifp->if_output = lpoutput;
23238061Smsmith	ifp->if_hdrlen = 0;
23338061Smsmith	ifp->if_addrlen = 0;
23438061Smsmith	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
23538061Smsmith	if_attach(ifp);
23638061Smsmith
23743773Sdes	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
23838061Smsmith
23955939Snsouch	return (0);
24038061Smsmith}
24138061Smsmith/*
24238061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol.
24338061Smsmith * We don't want to calculate these nasties in our tight loop, so we
24438061Smsmith * precalculate them when we initialize.
24538061Smsmith */
24638061Smsmithstatic int
24738061Smsmithlpinittables (void)
24838061Smsmith{
24938061Smsmith    int i;
25038061Smsmith
25138061Smsmith    if (!txmith)
25238061Smsmith	txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
25338061Smsmith
25438061Smsmith    if (!txmith)
25538061Smsmith	return 1;
25638061Smsmith
25738061Smsmith    if (!ctxmith)
25838061Smsmith	ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
25938061Smsmith
26038061Smsmith    if (!ctxmith)
26138061Smsmith	return 1;
26238061Smsmith
26338061Smsmith    for (i=0; i < LPIPTBLSIZE; i++) {
26438061Smsmith	ctxmith[i] = (i & 0xF0) >> 4;
26538061Smsmith	ctxmitl[i] = 0x10 | (i & 0x0F);
26638061Smsmith	ctrecvh[i] = (i & 0x78) << 1;
26738061Smsmith	ctrecvl[i] = (i & 0x78) >> 3;
26838061Smsmith    }
26938061Smsmith
27038061Smsmith    for (i=0; i < LPIPTBLSIZE; i++) {
27138061Smsmith	txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
27238061Smsmith	txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
27338061Smsmith	trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
27438061Smsmith	trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
27538061Smsmith    }
27638061Smsmith
27738061Smsmith    return 0;
27838061Smsmith}
27938061Smsmith
28038061Smsmith/*
28138061Smsmith * Process an ioctl request.
28238061Smsmith */
28338061Smsmith
28438061Smsmithstatic int
28538061Smsmithlpioctl (struct ifnet *ifp, u_long cmd, caddr_t data)
28638061Smsmith{
287121816Sbrooks    device_t dev = UNITODEVICE(ifp->if_dunit);
28855939Snsouch    device_t ppbus = device_get_parent(dev);
28955939Snsouch    struct lp_data *sc = DEVTOSOFTC(dev);
29038061Smsmith    struct ifaddr *ifa = (struct ifaddr *)data;
29138061Smsmith    struct ifreq *ifr = (struct ifreq *)data;
29238061Smsmith    u_char *ptr;
29355939Snsouch    void *ih;
29438061Smsmith    int error;
29538061Smsmith
29638061Smsmith    switch (cmd) {
29738061Smsmith
29838061Smsmith    case SIOCSIFDSTADDR:
29938061Smsmith    case SIOCAIFADDR:
30038061Smsmith    case SIOCSIFADDR:
30138061Smsmith	if (ifa->ifa_addr->sa_family != AF_INET)
30238061Smsmith	    return EAFNOSUPPORT;
30338061Smsmith
30438061Smsmith	ifp->if_flags |= IFF_UP;
30538061Smsmith	/* FALLTHROUGH */
30638061Smsmith    case SIOCSIFFLAGS:
307148887Srwatson	if ((!(ifp->if_flags & IFF_UP)) &&
308148887Srwatson	  (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
30938061Smsmith
31055939Snsouch	    ppb_wctr(ppbus, 0x00);
311148887Srwatson	    ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
31238061Smsmith
31338061Smsmith	    /* IFF_UP is not set, try to release the bus anyway */
31455939Snsouch	    ppb_release_bus(ppbus, dev);
31538061Smsmith	    break;
31638061Smsmith	}
317148887Srwatson	if (((ifp->if_flags & IFF_UP)) &&
318148887Srwatson	  (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
31938061Smsmith
32042443Snsouch	    /* XXX
32138061Smsmith	     * Should the request be interruptible?
32238061Smsmith	     */
32355939Snsouch	    if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT|PPB_INTR)))
32438061Smsmith		return (error);
32538061Smsmith
32642443Snsouch	    /* Now IFF_UP means that we own the bus */
32742443Snsouch
32855939Snsouch	    ppb_set_mode(ppbus, PPB_COMPATIBLE);
32942443Snsouch
33038061Smsmith	    if (lpinittables()) {
33155939Snsouch		ppb_release_bus(ppbus, dev);
33238061Smsmith		return ENOBUFS;
33338061Smsmith	    }
33438061Smsmith
335147256Sbrooks	    sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN,
336111119Simp				  M_DEVBUF, M_WAITOK);
33738061Smsmith	    if (!sc->sc_ifbuf) {
33855939Snsouch		ppb_release_bus(ppbus, dev);
33938061Smsmith		return ENOBUFS;
34038061Smsmith	    }
34138061Smsmith
34255939Snsouch	    /* attach our interrupt handler, later detached when the bus is released */
343155921Sjhb	    if ((error = bus_setup_intr(dev, sc->res_irq,
344166901Spiso					INTR_TYPE_NET, NULL, lp_intr, dev, &ih))) {
34555939Snsouch		ppb_release_bus(ppbus, dev);
34655939Snsouch		return (error);
34755939Snsouch	    }
34855939Snsouch
34955939Snsouch	    ppb_wctr(ppbus, IRQENABLE);
350148887Srwatson	    ifp->if_drv_flags |= IFF_DRV_RUNNING;
35138061Smsmith	}
35238061Smsmith	break;
35338061Smsmith
35438061Smsmith    case SIOCSIFMTU:
35538061Smsmith	ptr = sc->sc_ifbuf;
35638061Smsmith	sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT);
35738061Smsmith	if (!sc->sc_ifbuf) {
35838061Smsmith	    sc->sc_ifbuf = ptr;
35938061Smsmith	    return ENOBUFS;
36038061Smsmith	}
36138061Smsmith	if (ptr)
36238061Smsmith	    free(ptr,M_DEVBUF);
363147256Sbrooks	sc->sc_ifp->if_mtu = ifr->ifr_mtu;
36438061Smsmith	break;
36538061Smsmith
36638061Smsmith    case SIOCGIFMTU:
367147256Sbrooks	ifr->ifr_mtu = sc->sc_ifp->if_mtu;
36838061Smsmith	break;
36938061Smsmith
37038061Smsmith    case SIOCADDMULTI:
37138061Smsmith    case SIOCDELMULTI:
37238061Smsmith	if (ifr == 0) {
37338061Smsmith	    return EAFNOSUPPORT;		/* XXX */
37438061Smsmith	}
37538061Smsmith	switch (ifr->ifr_addr.sa_family) {
37638061Smsmith
37738061Smsmith	case AF_INET:
37838061Smsmith	    break;
37938061Smsmith
38038061Smsmith	default:
38138061Smsmith	    return EAFNOSUPPORT;
38238061Smsmith	}
38338061Smsmith	break;
38438061Smsmith
38540626Smsmith    case SIOCGIFMEDIA:
38640626Smsmith	/*
38740626Smsmith	 * No ifmedia support at this stage; maybe use it
38840626Smsmith	 * in future for eg. protocol selection.
38940626Smsmith	 */
39040626Smsmith	return EINVAL;
39140626Smsmith
39238061Smsmith    default:
39338373Sbde	lprintf("LP:ioctl(0x%lx)\n", cmd);
39438061Smsmith	return EINVAL;
39538061Smsmith    }
39638061Smsmith    return 0;
39738061Smsmith}
39838061Smsmith
39938061Smsmithstatic __inline int
40055939Snsouchclpoutbyte (u_char byte, int spin, device_t ppbus)
40138061Smsmith{
40255939Snsouch	ppb_wdtr(ppbus, ctxmitl[byte]);
40355939Snsouch	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
40438061Smsmith		if (--spin == 0) {
40538061Smsmith			return 1;
40638061Smsmith		}
40755939Snsouch	ppb_wdtr(ppbus, ctxmith[byte]);
40855939Snsouch	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
40938061Smsmith		if (--spin == 0) {
41038061Smsmith			return 1;
41138061Smsmith		}
41238061Smsmith	return 0;
41338061Smsmith}
41438061Smsmith
41538061Smsmithstatic __inline int
41655939Snsouchclpinbyte (int spin, device_t ppbus)
41738061Smsmith{
41842443Snsouch	u_char c, cl;
41938061Smsmith
42055939Snsouch	while((ppb_rstr(ppbus) & CLPIP_SHAKE))
42138061Smsmith	    if(!--spin) {
42238061Smsmith		return -1;
42338061Smsmith	    }
42455939Snsouch	cl = ppb_rstr(ppbus);
42555939Snsouch	ppb_wdtr(ppbus, 0x10);
42638061Smsmith
42755939Snsouch	while(!(ppb_rstr(ppbus) & CLPIP_SHAKE))
42838061Smsmith	    if(!--spin) {
42938061Smsmith		return -1;
43038061Smsmith	    }
43155939Snsouch	c = ppb_rstr(ppbus);
43255939Snsouch	ppb_wdtr(ppbus, 0x00);
43338061Smsmith
43438061Smsmith	return (ctrecvl[cl] | ctrecvh[c]);
43538061Smsmith}
43638061Smsmith
43738061Smsmithstatic void
43843773Sdeslptap(struct ifnet *ifp, struct mbuf *m)
43943773Sdes{
44043773Sdes	u_int32_t af = AF_INET;
441165640Sjhb	bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
44243773Sdes}
44343773Sdes
44443773Sdesstatic void
44555939Snsouchlp_intr (void *arg)
44638061Smsmith{
44755939Snsouch	device_t dev = (device_t)arg;
44855939Snsouch        device_t ppbus = device_get_parent(dev);
44955939Snsouch	struct lp_data *sc = DEVTOSOFTC(dev);
45038061Smsmith	int len, s, j;
45138061Smsmith	u_char *bp;
45238061Smsmith	u_char c, cl;
45338061Smsmith	struct mbuf *top;
45438061Smsmith
45538061Smsmith	s = splhigh();
45638061Smsmith
457147256Sbrooks	if (sc->sc_ifp->if_flags & IFF_LINK0) {
45838061Smsmith
45938061Smsmith	    /* Ack. the request */
46055939Snsouch	    ppb_wdtr(ppbus, 0x01);
46138061Smsmith
46238061Smsmith	    /* Get the packet length */
46355939Snsouch	    j = clpinbyte(LPMAXSPIN2, ppbus);
46438061Smsmith	    if (j == -1)
46538061Smsmith		goto err;
46638061Smsmith	    len = j;
46755939Snsouch	    j = clpinbyte(LPMAXSPIN2, ppbus);
46838061Smsmith	    if (j == -1)
46938061Smsmith		goto err;
47038061Smsmith	    len = len + (j << 8);
471147256Sbrooks	    if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN)
47238061Smsmith		goto err;
47338061Smsmith
47438061Smsmith	    bp  = sc->sc_ifbuf;
47538061Smsmith
47638061Smsmith	    while (len--) {
47755939Snsouch	        j = clpinbyte(LPMAXSPIN2, ppbus);
47838061Smsmith	        if (j == -1) {
47938061Smsmith		    goto err;
48038061Smsmith	        }
48138061Smsmith	        *bp++ = j;
48238061Smsmith	    }
48338061Smsmith	    /* Get and ignore checksum */
48455939Snsouch	    j = clpinbyte(LPMAXSPIN2, ppbus);
48538061Smsmith	    if (j == -1) {
48638061Smsmith	        goto err;
48738061Smsmith	    }
48838061Smsmith
48938061Smsmith	    len = bp - sc->sc_ifbuf;
49038061Smsmith	    if (len <= CLPIPHDRLEN)
49138061Smsmith	        goto err;
49238061Smsmith
49338061Smsmith	    sc->sc_iferrs = 0;
49438061Smsmith
49538061Smsmith	    len -= CLPIPHDRLEN;
496147256Sbrooks	    sc->sc_ifp->if_ipackets++;
497147256Sbrooks	    sc->sc_ifp->if_ibytes += len;
498147256Sbrooks	    top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp, 0);
49938061Smsmith	    if (top) {
500165632Sjhb		if (bpf_peers_present(sc->sc_ifp->if_bpf))
501147256Sbrooks		    lptap(sc->sc_ifp, top);
502134391Sandre		netisr_queue(NETISR_IP, top);	/* mbuf is free'd on failure. */
50338061Smsmith	    }
50438061Smsmith	    goto done;
50538061Smsmith	}
50655939Snsouch	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
507147256Sbrooks	    len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
50838061Smsmith	    bp  = sc->sc_ifbuf;
50938061Smsmith	    while (len--) {
51038061Smsmith
51155939Snsouch		cl = ppb_rstr(ppbus);
51255939Snsouch		ppb_wdtr(ppbus, 8);
51338061Smsmith
51438061Smsmith		j = LPMAXSPIN2;
51555939Snsouch		while((ppb_rstr(ppbus) & LPIP_SHAKE))
51638061Smsmith		    if(!--j) goto err;
51738061Smsmith
51855939Snsouch		c = ppb_rstr(ppbus);
51955939Snsouch		ppb_wdtr(ppbus, 0);
52038061Smsmith
52138061Smsmith		*bp++= trecvh[cl] | trecvl[c];
52238061Smsmith
52338061Smsmith		j = LPMAXSPIN2;
52455939Snsouch		while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) {
52538061Smsmith		    if (cl != c &&
52655939Snsouch			(((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
52738061Smsmith			  (c & 0xf8))
52838061Smsmith			goto end;
52938061Smsmith		    if (!--j) goto err;
53038061Smsmith		}
53138061Smsmith	    }
53238061Smsmith
53338061Smsmith	end:
53438061Smsmith	    len = bp - sc->sc_ifbuf;
53538061Smsmith	    if (len <= LPIPHDRLEN)
53638061Smsmith		goto err;
53738061Smsmith
53838061Smsmith	    sc->sc_iferrs = 0;
53938061Smsmith
54038061Smsmith	    len -= LPIPHDRLEN;
541147256Sbrooks	    sc->sc_ifp->if_ipackets++;
542147256Sbrooks	    sc->sc_ifp->if_ibytes += len;
543147256Sbrooks	    top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp, 0);
54438061Smsmith	    if (top) {
545165632Sjhb		if (bpf_peers_present(sc->sc_ifp->if_bpf))
546147256Sbrooks		    lptap(sc->sc_ifp, top);
547134391Sandre		netisr_queue(NETISR_IP, top);	/* mbuf is free'd on failure. */
54838061Smsmith	    }
54938061Smsmith	}
55038061Smsmith	goto done;
55138061Smsmith
55238061Smsmith    err:
55355939Snsouch	ppb_wdtr(ppbus, 0);
55438061Smsmith	lprintf("R");
555147256Sbrooks	sc->sc_ifp->if_ierrors++;
55638061Smsmith	sc->sc_iferrs++;
55738061Smsmith
55838061Smsmith	/*
55938061Smsmith	 * We are not able to send receive anything for now,
56038061Smsmith	 * so stop wasting our time
56138061Smsmith	 */
56238061Smsmith	if (sc->sc_iferrs > LPMAXERRS) {
56355939Snsouch	    printf("lp%d: Too many errors, Going off-line.\n", device_get_unit(dev));
56455939Snsouch	    ppb_wctr(ppbus, 0x00);
565148887Srwatson	    sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
56638061Smsmith	    sc->sc_iferrs=0;
56738061Smsmith	}
56838061Smsmith
56938061Smsmith    done:
57038061Smsmith	splx(s);
57138061Smsmith	return;
57238061Smsmith}
57338061Smsmith
57438061Smsmithstatic __inline int
57555939Snsouchlpoutbyte (u_char byte, int spin, device_t ppbus)
57638061Smsmith{
57755939Snsouch    ppb_wdtr(ppbus, txmith[byte]);
57855939Snsouch    while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
57938061Smsmith	if (--spin == 0)
58038061Smsmith		return 1;
58155939Snsouch    ppb_wdtr(ppbus, txmitl[byte]);
58255939Snsouch    while (ppb_rstr(ppbus) & LPIP_SHAKE)
58338061Smsmith	if (--spin == 0)
58438061Smsmith		return 1;
58538061Smsmith    return 0;
58638061Smsmith}
58738061Smsmith
58838061Smsmithstatic int
58938061Smsmithlpoutput (struct ifnet *ifp, struct mbuf *m,
59038061Smsmith	  struct sockaddr *dst, struct rtentry *rt)
59138061Smsmith{
592121816Sbrooks    device_t dev = UNITODEVICE(ifp->if_dunit);
59355939Snsouch    device_t ppbus = device_get_parent(dev);
59438061Smsmith    int s, err;
59538061Smsmith    struct mbuf *mm;
59638061Smsmith    u_char *cp = "\0\0";
59738061Smsmith    u_char chksum = 0;
59838061Smsmith    int count = 0;
59943773Sdes    int i, len, spin;
60038061Smsmith
60138061Smsmith    /* We need a sensible value if we abort */
60238061Smsmith    cp++;
603148887Srwatson    ifp->if_drv_flags |= IFF_DRV_RUNNING;
60438061Smsmith
60538061Smsmith    err = 1;			/* assume we're aborting because of an error */
60638061Smsmith
60738061Smsmith    s = splhigh();
60838061Smsmith
60938061Smsmith    /* Suspend (on laptops) or receive-errors might have taken us offline */
61055939Snsouch    ppb_wctr(ppbus, IRQENABLE);
61138061Smsmith
61238061Smsmith    if (ifp->if_flags & IFF_LINK0) {
61338061Smsmith
61455939Snsouch	if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
61538061Smsmith	    lprintf("&");
61655939Snsouch	    lp_intr(dev);
61738061Smsmith	}
61838061Smsmith
61938061Smsmith	/* Alert other end to pending packet */
62038061Smsmith	spin = LPMAXSPIN1;
62155939Snsouch	ppb_wdtr(ppbus, 0x08);
62255939Snsouch	while ((ppb_rstr(ppbus) & 0x08) == 0)
62338061Smsmith		if (--spin == 0) {
62438061Smsmith			goto nend;
62538061Smsmith		}
62638061Smsmith
62738061Smsmith	/* Calculate length of packet, then send that */
62838061Smsmith
62938061Smsmith	count += 14;		/* Ethernet header len */
63038061Smsmith
63138061Smsmith	mm = m;
63238061Smsmith	for (mm = m; mm; mm = mm->m_next) {
63338061Smsmith		count += mm->m_len;
63438061Smsmith	}
63555939Snsouch	if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
63638061Smsmith		goto nend;
63755939Snsouch	if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
63838061Smsmith		goto nend;
63938061Smsmith
64038061Smsmith	/* Send dummy ethernet header */
64138061Smsmith	for (i = 0; i < 12; i++) {
64255939Snsouch		if (clpoutbyte(i, LPMAXSPIN1, ppbus))
64338061Smsmith			goto nend;
64438061Smsmith		chksum += i;
64538061Smsmith	}
64638061Smsmith
64755939Snsouch	if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
64838061Smsmith		goto nend;
64955939Snsouch	if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
65038061Smsmith		goto nend;
65138061Smsmith	chksum += 0x08 + 0x00;		/* Add into checksum */
65238061Smsmith
65338061Smsmith	mm = m;
65438061Smsmith	do {
65538061Smsmith		cp = mtod(mm, u_char *);
65643773Sdes		len = mm->m_len;
65743773Sdes		while (len--) {
65838061Smsmith			chksum += *cp;
65955939Snsouch			if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
66038061Smsmith				goto nend;
66138061Smsmith		}
66238061Smsmith	} while ((mm = mm->m_next));
66338061Smsmith
66438061Smsmith	/* Send checksum */
66555939Snsouch	if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
66638061Smsmith		goto nend;
66738061Smsmith
66838061Smsmith	/* Go quiescent */
66955939Snsouch	ppb_wdtr(ppbus, 0);
67038061Smsmith
67138061Smsmith	err = 0;			/* No errors */
67238061Smsmith
67338061Smsmith	nend:
67438061Smsmith	if (err)  {				/* if we didn't timeout... */
67538061Smsmith		ifp->if_oerrors++;
67638061Smsmith		lprintf("X");
67738061Smsmith	} else {
67838061Smsmith		ifp->if_opackets++;
67938061Smsmith		ifp->if_obytes += m->m_pkthdr.len;
680165632Sjhb		if (bpf_peers_present(ifp->if_bpf))
68143773Sdes		    lptap(ifp, m);
68238061Smsmith	}
68338061Smsmith
68438061Smsmith	m_freem(m);
68538061Smsmith
68655939Snsouch	if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
68738061Smsmith		lprintf("^");
68855939Snsouch		lp_intr(dev);
68938061Smsmith	}
69038061Smsmith	(void) splx(s);
69138061Smsmith	return 0;
69238061Smsmith    }
69338061Smsmith
69455939Snsouch    if (ppb_rstr(ppbus) & LPIP_SHAKE) {
69538061Smsmith        lprintf("&");
69655939Snsouch        lp_intr(dev);
69738061Smsmith    }
69838061Smsmith
69955939Snsouch    if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
70038061Smsmith        goto end;
70155939Snsouch    if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
70238061Smsmith        goto end;
70338061Smsmith
70438061Smsmith    mm = m;
70538061Smsmith    do {
70638061Smsmith        cp = mtod(mm,u_char *);
70743773Sdes	len = mm->m_len;
70843773Sdes        while (len--)
70955939Snsouch	    if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
71038061Smsmith	        goto end;
71138061Smsmith    } while ((mm = mm->m_next));
71238061Smsmith
71338061Smsmith    err = 0;				/* no errors were encountered */
71438061Smsmith
71538061Smsmith    end:
71638061Smsmith    --cp;
71755939Snsouch    ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
71838061Smsmith
71938061Smsmith    if (err)  {				/* if we didn't timeout... */
72038061Smsmith	ifp->if_oerrors++;
72138061Smsmith        lprintf("X");
72238061Smsmith    } else {
72338061Smsmith	ifp->if_opackets++;
72438061Smsmith	ifp->if_obytes += m->m_pkthdr.len;
725165632Sjhb	if (bpf_peers_present(ifp->if_bpf))
72643773Sdes	    lptap(ifp, m);
72738061Smsmith    }
72838061Smsmith
72938061Smsmith    m_freem(m);
73038061Smsmith
73155939Snsouch    if (ppb_rstr(ppbus) & LPIP_SHAKE) {
73238061Smsmith	lprintf("^");
73355939Snsouch	lp_intr(dev);
73438061Smsmith    }
73538061Smsmith
73638061Smsmith    (void) splx(s);
73738061Smsmith    return 0;
73838061Smsmith}
73955939Snsouch
74056455Speterstatic device_method_t lp_methods[] = {
74156455Speter  	/* device interface */
74256455Speter	DEVMETHOD(device_identify,	lp_identify),
74356455Speter	DEVMETHOD(device_probe,		lp_probe),
74456455Speter	DEVMETHOD(device_attach,	lp_attach),
74556455Speter
74656455Speter	{ 0, 0 }
74756455Speter};
74856455Speter
74956455Speterstatic driver_t lp_driver = {
75056455Speter  "plip",
75156455Speter  lp_methods,
75256455Speter  sizeof(struct lp_data),
75356455Speter};
75456455Speter
75555939SnsouchDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0);
756153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1);
757