if_plip.c revision 106937
1139743Simp/*-
265577Sdes * Copyright (c) 1997 Poul-Henning Kamp
365577Sdes * All rights reserved.
459412Smsmith *
559412Smsmith * Redistribution and use in source and binary forms, with or without
659412Smsmith * modification, are permitted provided that the following conditions
759412Smsmith * are met:
859412Smsmith * 1. Redistributions of source code must retain the above copyright
959412Smsmith *    notice, this list of conditions and the following disclaimer.
1059412Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1159412Smsmith *    notice, this list of conditions and the following disclaimer in the
1259412Smsmith *    documentation and/or other materials provided with the distribution.
1359412Smsmith *
1459412Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1559412Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1659412Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1759412Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1859412Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1959412Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2059412Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2159412Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2259412Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2359412Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2459412Smsmith * SUCH DAMAGE.
2559412Smsmith *
2659412Smsmith *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
2759412Smsmith * $FreeBSD: head/sys/dev/ppbus/if_plip.c 106937 2002-11-14 23:54:55Z sam $
2859412Smsmith */
2959412Smsmith
3059412Smsmith/*
3159412Smsmith * Parallel port TCP/IP interfaces added.  I looked at the driver from
3259412Smsmith * MACH but this is a complete rewrite, and btw. incompatible, and it
3359412Smsmith * should perform better too.  I have never run the MACH driver though.
3459412Smsmith *
3559412Smsmith * This driver sends two bytes (0x08, 0x00) in front of each packet,
3659412Smsmith * to allow us to distinguish another format later.
3759412Smsmith *
3859412Smsmith * Now added an Linux/Crynwr compatibility mode which is enabled using
3959412Smsmith * IF_LINK0 - Tim Wilkinson.
4059412Smsmith *
4159412Smsmith * TODO:
42116173Sobrien *    Make HDLC/PPP mode, use IF_LLC1 to enable.
43116173Sobrien *
44116173Sobrien * Connect the two computers using a Laplink parallel cable to use this
4559412Smsmith * feature:
4683926Sdes *
4776166Smarkm *      +----------------------------------------+
4874135Sjlemon * 	|A-name	A-End	B-End	Descr.	Port/Bit |
4983926Sdes *      +----------------------------------------+
50119911Sdes *	|DATA0	2	15	Data	0/0x01   |
5176166Smarkm *	|-ERROR	15	2	   	1/0x08   |
5265633Sdes *      +----------------------------------------+
5383926Sdes *	|DATA1	3	13	Data	0/0x02	 |
5476166Smarkm *	|+SLCT	13	3	   	1/0x10   |
5574135Sjlemon *      +----------------------------------------+
5678025Sdes *	|DATA2	4	12	Data	0/0x04   |
5776827Salfred *	|+PE	12	4	   	1/0x20   |
5885289Sdes *      +----------------------------------------+
5965633Sdes *	|DATA3	5	10	Strobe	0/0x08   |
6065633Sdes *	|-ACK	10	5	   	1/0x40   |
6169995Sdes *      +----------------------------------------+
62123246Sdes *	|DATA4	6	11	Data	0/0x10   |
6383926Sdes *	|BUSY	11	6	   	1/~0x80  |
6476839Sjlemon *      +----------------------------------------+
6583926Sdes *	|GND	18-25	18-25	GND	-        |
6665633Sdes *      +----------------------------------------+
6783926Sdes *
6883926Sdes * Expect transfer-rates up to 75 kbyte/sec.
6959412Smsmith *
7059412Smsmith * If GCC could correctly grok
7183926Sdes *	register int port asm("edx")
7283926Sdes * the code would be cleaner
7359412Smsmith *
7459412Smsmith * Poul-Henning Kamp <phk@freebsd.org>
7567588Sdes */
7659412Smsmith
7760860Sdes/*
7859412Smsmith * Update for ppbus, PLIP support only - Nicolas Souchu
7969799Sdes */
8067589Sdes
8178113Sdes#include "opt_plip.h"
82133822Stjr
8367589Sdes#include <sys/param.h>
8459412Smsmith#include <sys/systm.h>
85133822Stjr#include <sys/module.h>
8659412Smsmith#include <sys/bus.h>
87133822Stjr#include <sys/mbuf.h>
88140214Sobrien#include <sys/socket.h>
89140214Sobrien#include <sys/sockio.h>
90140214Sobrien#include <sys/kernel.h>
9187275Srwatson#include <sys/malloc.h>
92133822Stjr
9385129Sdes#include <machine/bus.h>
9469995Sdes#include <machine/resource.h>
9585289Sdes#include <sys/rman.h>
9678025Sdes
9784248Sdes#include <net/if.h>
9859412Smsmith#include <net/if_types.h>
9967588Sdes#include <net/netisr.h>
10067588Sdes
10167588Sdes#include <netinet/in.h>
10276405Sdes#include <netinet/in_var.h>
10367588Sdes
10467588Sdes#include <net/bpf.h>
10569799Sdes
10667588Sdes#include <dev/ppbus/ppbconf.h>
10767588Sdes#include "ppbus_if.h"
10874135Sjlemon#include <dev/ppbus/ppbio.h>
10978113Sdes
11078113Sdes#ifndef LPMTU			/* MTU for the lp# interfaces */
11178113Sdes#define	LPMTU	1500
11278025Sdes#endif
11378025Sdes
11459412Smsmith#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
11559412Smsmith#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
11659412Smsmith#endif
11759412Smsmith
11859412Smsmith#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
11959412Smsmith#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
120113574Sjhb#endif
121113574Sjhb
122113574Sjhb#ifndef LPMAXERRS		/* Max errors before !RUNNING */
12360860Sdes#define	LPMAXERRS	100
124117723Sphk#endif
12559412Smsmith
12659412Smsmith#define CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
12759412Smsmith#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
12859412Smsmith#define MLPIPHDRLEN	CLPIPHDRLEN
12959412Smsmith
13059412Smsmith#define LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
13159412Smsmith#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
13259412Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
13359412Smsmith#define MLPIPHDRLEN	LPIPHDRLEN
13459412Smsmith#endif
13559412Smsmith
13659412Smsmith#define	LPIPTBLSIZE	256	/* Size of octet translation table */
13759412Smsmith
13859412Smsmith#define lprintf		if (lptflag) printf
139117723Sphk
140153310Smlaier#ifdef PLIP_DEBUG
141153310Smlaierstatic int volatile lptflag = 1;
142117723Sphk#else
14360860Sdesstatic int volatile lptflag = 0;
144124082Salc#endif
14571471Sjhb
14660860Sdesstruct lp_data {
14760860Sdes	unsigned short lp_unit;
148124082Salc
14960860Sdes	struct  ifnet	sc_if;
15059412Smsmith	u_char		*sc_ifbuf;
15159412Smsmith	int		sc_iferrs;
15259412Smsmith
15359412Smsmith	struct resource *res_irq;
15459412Smsmith};
15559412Smsmith
15659412Smsmith/* Tables for the lp# interface */
15759412Smsmithstatic u_char *txmith;
15859412Smsmith#define txmitl (txmith+(1*LPIPTBLSIZE))
15959412Smsmith#define trecvh (txmith+(2*LPIPTBLSIZE))
16059412Smsmith#define trecvl (txmith+(3*LPIPTBLSIZE))
16178025Sdes
16278031Sdesstatic u_char *ctxmith;
16369799Sdes#define ctxmitl (ctxmith+(1*LPIPTBLSIZE))
16476839Sjlemon#define ctrecvh (ctxmith+(2*LPIPTBLSIZE))
16569799Sdes#define ctrecvl (ctxmith+(3*LPIPTBLSIZE))
16669799Sdes
16769799Sdes/* Functions for the lp# interface */
16869799Sdesstatic int lpinittables(void);
16969799Sdesstatic int lpioctl(struct ifnet *, u_long, caddr_t);
17076839Sjlemonstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
17176839Sjlemon	struct rtentry *);
17269799Sdesstatic void lp_intr(void *);
17369799Sdes
17469799Sdes#define DEVTOSOFTC(dev) \
17569799Sdes	((struct lp_data *)device_get_softc(dev))
17669799Sdes#define UNITOSOFTC(unit) \
17759412Smsmith	((struct lp_data *)devclass_get_softc(lp_devclass, (unit)))
17878025Sdes#define UNITODEVICE(unit) \
17959412Smsmith	(devclass_get_device(lp_devclass, (unit)))
18059412Smsmith
181133822Stjrstatic devclass_t lp_devclass;
18278113Sdes
183133822Stjrstatic void
18478113Sdeslp_identify(driver_t *driver, device_t parent)
18578113Sdes{
18678113Sdes
18778113Sdes	BUS_ADD_CHILD(parent, 0, "plip", -1);
188159544Sdes}
189159544Sdes/*
190159544Sdes * lpprobe()
191123246Sdes */
192118421Sdesstatic int
19359412Smsmithlp_probe(device_t dev)
19469799Sdes{
19578031Sdes	device_t ppbus = device_get_parent(dev);
19678031Sdes	struct lp_data *lp;
19769799Sdes	int zero = 0;
19878031Sdes	uintptr_t irq;
19978031Sdes
20078031Sdes	lp = DEVTOSOFTC(dev);
20178031Sdes	bzero(lp, sizeof(struct lp_data));
20278031Sdes
20378031Sdes	/* retrieve the ppbus irq */
20478031Sdes	BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
20567589Sdes
20667589Sdes	/* if we haven't interrupts, the probe fails */
20767589Sdes	if (irq == -1) {
20859412Smsmith		device_printf(dev, "not an interrupt driven port, failed.\n");
209133822Stjr		return (ENXIO);
21059412Smsmith	}
21167589Sdes
21259412Smsmith	/* reserve the interrupt resource, expecting irq is available to continue */
21359412Smsmith	lp->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
21467589Sdes					 RF_SHAREABLE);
21559412Smsmith	if (lp->res_irq == 0) {
21659412Smsmith		device_printf(dev, "cannot reserve interrupt, failed.\n");
21767589Sdes		return (ENXIO);
21859412Smsmith	}
21959412Smsmith
22067589Sdes	/*
22159412Smsmith	 * lp dependent initialisation.
22259412Smsmith	 */
22367589Sdes	lp->lp_unit = device_get_unit(dev);
22459412Smsmith
22559412Smsmith	device_set_desc(dev, "PLIP network interface");
22678031Sdes
22759412Smsmith	return (0);
228159170Sdes}
229133822Stjr
230159170Sdesstatic int
231133822Stjrlp_attach (device_t dev)
232133822Stjr{
23359412Smsmith	struct lp_data *lp = DEVTOSOFTC(dev);
23459412Smsmith	struct ifnet *ifp = &lp->sc_if;
235159544Sdes
236159544Sdes	ifp->if_softc = lp;
237159544Sdes	ifp->if_name = "lp";
238159544Sdes	ifp->if_unit = device_get_unit(dev);
239159544Sdes	ifp->if_mtu = LPMTU;
240159544Sdes	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
241123246Sdes	ifp->if_ioctl = lpioctl;
242118421Sdes	ifp->if_output = lpoutput;
243118421Sdes	ifp->if_type = IFT_PARA;
244118421Sdes	ifp->if_hdrlen = 0;
245118421Sdes	ifp->if_addrlen = 0;
246118421Sdes	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
247159544Sdes	if_attach(ifp);
248118421Sdes
249159544Sdes	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
250159544Sdes
251118421Sdes	return (0);
25259412Smsmith}
25378031Sdes/*
25478031Sdes * Build the translation tables for the LPIP (BSD unix) protocol.
25567589Sdes * We don't want to calculate these nasties in our tight loop, so we
25678031Sdes * precalculate them when we initialize.
25767589Sdes */
25878031Sdesstatic int
25967589Sdeslpinittables (void)
26078031Sdes{
261119068Sdes    int i;
26278031Sdes
26367589Sdes    if (!txmith)
26478025Sdes	txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
26578025Sdes
26678031Sdes    if (!txmith)
26769799Sdes	return 1;
26869799Sdes
26978025Sdes    if (!ctxmith)
27069799Sdes	ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
27169799Sdes
27269799Sdes    if (!ctxmith)
27378031Sdes	return 1;
27469995Sdes
27578025Sdes    for (i=0; i < LPIPTBLSIZE; i++) {
27659412Smsmith	ctxmith[i] = (i & 0xF0) >> 4;
277133822Stjr	ctxmitl[i] = 0x10 | (i & 0x0F);
27865633Sdes	ctrecvh[i] = (i & 0x78) << 1;
27978113Sdes	ctrecvl[i] = (i & 0x78) >> 3;
28085289Sdes    }
28185289Sdes
28285289Sdes    for (i=0; i < LPIPTBLSIZE; i++) {
28385289Sdes	txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
28485289Sdes	txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
28585289Sdes	trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
28685289Sdes	trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
28785289Sdes    }
28885289Sdes
28985289Sdes    return 0;
29091334Sjulian}
29191334Sjulian
29285289Sdes/*
29385289Sdes * Process an ioctl request.
29485289Sdes */
29585289Sdes
29685289Sdesstatic int
29785289Sdeslpioctl (struct ifnet *ifp, u_long cmd, caddr_t data)
298124407Srwatson{
29985289Sdes    device_t dev = UNITODEVICE(ifp->if_unit);
30091334Sjulian    device_t ppbus = device_get_parent(dev);
30191334Sjulian    struct lp_data *sc = DEVTOSOFTC(dev);
30285289Sdes    struct ifaddr *ifa = (struct ifaddr *)data;
303119068Sdes    struct ifreq *ifr = (struct ifreq *)data;
30485289Sdes    u_char *ptr;
30585289Sdes    void *ih;
30685289Sdes    int error;
30785289Sdes
30885289Sdes    switch (cmd) {
309119068Sdes
31085289Sdes    case SIOCSIFDSTADDR:
31185289Sdes    case SIOCAIFADDR:
31285289Sdes    case SIOCSIFADDR:
31385289Sdes	if (ifa->ifa_addr->sa_family != AF_INET)
31485289Sdes	    return EAFNOSUPPORT;
31585289Sdes
31685289Sdes	ifp->if_flags |= IFF_UP;
31785289Sdes	/* FALLTHROUGH */
31885289Sdes    case SIOCSIFFLAGS:
31985289Sdes	if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) {
32085289Sdes
32185289Sdes	    ppb_wctr(ppbus, 0x00);
322119068Sdes	    ifp->if_flags &= ~IFF_RUNNING;
323158311Sambrisko
324158311Sambrisko	    /* IFF_UP is not set, try to release the bus anyway */
325158311Sambrisko	    ppb_release_bus(ppbus, dev);
326158311Sambrisko	    break;
327158311Sambrisko	}
328158311Sambrisko	if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
329158311Sambrisko
33085289Sdes	    /* XXX
33185289Sdes	     * Should the request be interruptible?
33285289Sdes	     */
33385289Sdes	    if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT|PPB_INTR)))
33485289Sdes		return (error);
33585289Sdes
33685289Sdes	    /* Now IFF_UP means that we own the bus */
33785289Sdes
33885289Sdes	    ppb_set_mode(ppbus, PPB_COMPATIBLE);
33985289Sdes
34085289Sdes	    if (lpinittables()) {
34185289Sdes		ppb_release_bus(ppbus, dev);
34285289Sdes		return ENOBUFS;
34385289Sdes	    }
34485289Sdes
34585289Sdes	    sc->sc_ifbuf = malloc(sc->sc_if.if_mtu + MLPIPHDRLEN,
34685289Sdes				  M_DEVBUF, M_WAITOK);
34785289Sdes	    if (!sc->sc_ifbuf) {
34885289Sdes		ppb_release_bus(ppbus, dev);
34985289Sdes		return ENOBUFS;
35085289Sdes	    }
35178113Sdes
35278113Sdes	    /* attach our interrupt handler, later detached when the bus is released */
35378025Sdes	    if ((error = BUS_SETUP_INTR(ppbus, dev, sc->res_irq,
35478025Sdes					INTR_TYPE_NET, lp_intr, dev, &ih))) {
35565633Sdes		ppb_release_bus(ppbus, dev);
356123246Sdes		return (error);
357120339Sdes	    }
358120339Sdes
359120339Sdes	    ppb_wctr(ppbus, IRQENABLE);
360120339Sdes	    ifp->if_flags |= IFF_RUNNING;
361120339Sdes	}
362120339Sdes	break;
363143194Ssobomax
364143194Ssobomax    case SIOCSIFMTU:
365143194Ssobomax	ptr = sc->sc_ifbuf;
366143194Ssobomax	sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT);
367143194Ssobomax	if (!sc->sc_ifbuf) {
368143194Ssobomax	    sc->sc_ifbuf = ptr;
36978025Sdes	    return ENOBUFS;
37069799Sdes	}
37169799Sdes	if (ptr)
37269799Sdes	    free(ptr,M_DEVBUF);
37369799Sdes	sc->sc_if.if_mtu = ifr->ifr_mtu;
37469799Sdes	break;
37585657Sdillon
37669799Sdes    case SIOCGIFMTU:
37769799Sdes	ifr->ifr_mtu = sc->sc_if.if_mtu;
37869799Sdes	break;
37969799Sdes
38069799Sdes    case SIOCADDMULTI:
38169799Sdes    case SIOCDELMULTI:
382113574Sjhb	if (ifr == 0) {
38378025Sdes	    return EAFNOSUPPORT;		/* XXX */
38465633Sdes	}
38565633Sdes	switch (ifr->ifr_addr.sa_family) {
38678113Sdes
38778113Sdes	case AF_INET:
38878113Sdes	    break;
38978025Sdes
39078025Sdes	default:
39165633Sdes	    return EAFNOSUPPORT;
39265633Sdes	}
39365633Sdes	break;
39465633Sdes
39585657Sdillon    case SIOCGIFMEDIA:
396113574Sjhb	/*
39769799Sdes	 * No ifmedia support at this stage; maybe use it
39878025Sdes	 * in future for eg. protocol selection.
39965633Sdes	 */
40065633Sdes	return EINVAL;
40178113Sdes
40278113Sdes    default:
40378113Sdes	lprintf("LP:ioctl(0x%lx)\n", cmd);
40478025Sdes	return EINVAL;
40578025Sdes    }
40665633Sdes    return 0;
40787275Srwatson}
40887275Srwatson
40987275Srwatsonstatic __inline int
410112206Sjhbclpoutbyte (u_char byte, int spin, device_t ppbus)
411112206Sjhb{
41287275Srwatson	ppb_wdtr(ppbus, ctxmitl[byte]);
41378025Sdes	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
41469995Sdes		if (--spin == 0) {
41587275Srwatson			return 1;
41678025Sdes		}
41765633Sdes	ppb_wdtr(ppbus, ctxmith[byte]);
41865633Sdes	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
41978113Sdes		if (--spin == 0) {
42078113Sdes			return 1;
42178113Sdes		}
42278025Sdes	return 0;
42378025Sdes}
42476839Sjlemon
42578025Sdesstatic __inline int
42676839Sjlemonclpinbyte (int spin, device_t ppbus)
42776839Sjlemon{
42876839Sjlemon	u_char c, cl;
42976839Sjlemon
43076839Sjlemon	while((ppb_rstr(ppbus) & CLPIP_SHAKE))
43176839Sjlemon	    if(!--spin) {
43276839Sjlemon		return -1;
43376839Sjlemon	    }
43476839Sjlemon	cl = ppb_rstr(ppbus);
43578116Sdes	ppb_wdtr(ppbus, 0x10);
43676839Sjlemon
437119068Sdes	while(!(ppb_rstr(ppbus) & CLPIP_SHAKE))
43878025Sdes	    if(!--spin) {
43976839Sjlemon		return -1;
44076839Sjlemon	    }
44178113Sdes	c = ppb_rstr(ppbus);
44278113Sdes	ppb_wdtr(ppbus, 0x00);
44378113Sdes
44478025Sdes	return (ctrecvl[cl] | ctrecvh[c]);
44578025Sdes}
44667588Sdes
44769995Sdesstatic void
44867588Sdeslptap(struct ifnet *ifp, struct mbuf *m)
44994307Sjhb{
45069995Sdes	/*
45178025Sdes	 * Send a packet through bpf. We need to prepend the address family
45278025Sdes	 * as a four byte field. Cons up a dummy header to pacify bpf. This
45367588Sdes	 * is safe because bpf will only read from the mbuf (i.e., it won't
45467588Sdes	 * try to free it or keep a pointer to it).
45573923Sjhb	 */
45667588Sdes	u_int32_t af = AF_INET;
45767588Sdes	struct mbuf m0;
45891140Stanimura
45967588Sdes	m0.m_next = m;
46067588Sdes	m0.m_len = sizeof(u_int32_t);
46167588Sdes	m0.m_data = (char *)&af;
46267588Sdes	BPF_MTAP(ifp, &m0);
46367588Sdes}
46467588Sdes
46567588Sdesstatic void
46667588Sdeslp_intr (void *arg)
46767588Sdes{
46867588Sdes	device_t dev = (device_t)arg;
46967588Sdes        device_t ppbus = device_get_parent(dev);
47067588Sdes	struct lp_data *sc = DEVTOSOFTC(dev);
47167588Sdes	int len, s, j;
47267588Sdes	u_char *bp;
47367588Sdes	u_char c, cl;
47467588Sdes	struct mbuf *top;
475113574Sjhb
476113574Sjhb	s = splhigh();
47767588Sdes
47869995Sdes	if (sc->sc_if.if_flags & IFF_LINK0) {
47967588Sdes
48067588Sdes	    /* Ack. the request */
48169799Sdes	    ppb_wdtr(ppbus, 0x01);
48269799Sdes
48367588Sdes	    /* Get the packet length */
48467588Sdes	    j = clpinbyte(LPMAXSPIN2, ppbus);
48567588Sdes	    if (j == -1)
48667588Sdes		goto err;
48767588Sdes	    len = j;
48869799Sdes	    j = clpinbyte(LPMAXSPIN2, ppbus);
48969799Sdes	    if (j == -1)
49069799Sdes		goto err;
49169799Sdes	    len = len + (j << 8);
49267588Sdes	    if (len > sc->sc_if.if_mtu + MLPIPHDRLEN)
49378025Sdes		goto err;
494119068Sdes
49578025Sdes	    bp  = sc->sc_ifbuf;
49667588Sdes
49767588Sdes	    while (len--) {
49867588Sdes	        j = clpinbyte(LPMAXSPIN2, ppbus);
499119911Sdes	        if (j == -1) {
500119911Sdes		    goto err;
501119911Sdes	        }
502119911Sdes	        *bp++ = j;
503119911Sdes	    }
504119911Sdes	    /* Get and ignore checksum */
505119911Sdes	    j = clpinbyte(LPMAXSPIN2, ppbus);
506120340Sdes	    if (j == -1) {
507119911Sdes	        goto err;
508119911Sdes	    }
509119911Sdes
510119911Sdes	    len = bp - sc->sc_ifbuf;
511119911Sdes	    if (len <= CLPIPHDRLEN)
512119911Sdes	        goto err;
513119911Sdes
514119911Sdes	    sc->sc_iferrs = 0;
515119911Sdes
516119911Sdes	    len -= CLPIPHDRLEN;
517119911Sdes	    sc->sc_if.if_ipackets++;
518119911Sdes	    sc->sc_if.if_ibytes += len;
519119911Sdes	    top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, &sc->sc_if, 0);
520119911Sdes	    if (top) {
521119911Sdes		if (sc->sc_if.if_bpf)
522119911Sdes		    lptap(&sc->sc_if, top);
523119911Sdes                if (! IF_HANDOFF(&ipintrq, top, NULL)) {
524119911Sdes	            lprintf("DROP");
525119911Sdes                } else {
526119911Sdes	            schednetisr(NETISR_IP);
527119911Sdes	        }
528119911Sdes	    }
529119911Sdes	    goto done;
53078113Sdes	}
53178113Sdes	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
53278025Sdes	    len = sc->sc_if.if_mtu + LPIPHDRLEN;
53378025Sdes	    bp  = sc->sc_ifbuf;
53467588Sdes	    while (len--) {
53569995Sdes
53667588Sdes		cl = ppb_rstr(ppbus);
53769799Sdes		ppb_wdtr(ppbus, 8);
53899072Sjulian
539114983Sjhb		j = LPMAXSPIN2;
54074135Sjlemon		while((ppb_rstr(ppbus) & LPIP_SHAKE))
54167588Sdes		    if(!--j) goto err;
542113611Sjhb
54399072Sjulian		c = ppb_rstr(ppbus);
54499072Sjulian		ppb_wdtr(ppbus, 0);
54599072Sjulian
54699072Sjulian		*bp++= trecvh[cl] | trecvl[c];
54799072Sjulian
548113611Sjhb		j = LPMAXSPIN2;
54999072Sjulian		while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) {
55099072Sjulian		    if (cl != c &&
55199072Sjulian			(((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
55299072Sjulian			  (c & 0xf8))
55399072Sjulian			goto end;
55499072Sjulian		    if (!--j) goto err;
55599072Sjulian		}
55699072Sjulian	    }
55799072Sjulian
55899072Sjulian	end:
559103216Sjulian	    len = bp - sc->sc_ifbuf;
56099072Sjulian	    if (len <= LPIPHDRLEN)
56199072Sjulian		goto err;
56299072Sjulian
56399072Sjulian	    sc->sc_iferrs = 0;
56499072Sjulian
56599072Sjulian	    len -= LPIPHDRLEN;
56699072Sjulian	    sc->sc_if.if_ipackets++;
56799072Sjulian	    sc->sc_if.if_ibytes += len;
56899072Sjulian	    top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, &sc->sc_if, 0);
56999072Sjulian	    if (top) {
57099072Sjulian		if (sc->sc_if.if_bpf)
57199072Sjulian		    lptap(&sc->sc_if, top);
57299072Sjulian                if (! IF_HANDOFF(&ipintrq, top, NULL))  {
57399072Sjulian		    lprintf("DROP");
57499072Sjulian                } else {
57599072Sjulian		    schednetisr(NETISR_IP);
57699072Sjulian	        }
57799072Sjulian	    }
578113611Sjhb	}
57999072Sjulian	goto done;
58067588Sdes
58169995Sdes    err:
58278025Sdes	ppb_wdtr(ppbus, 0);
58378031Sdes	lprintf("R");
58467588Sdes	sc->sc_if.if_ierrors++;
58567588Sdes	sc->sc_iferrs++;
58667588Sdes
58767588Sdes	/*
58878025Sdes	 * We are not able to send receive anything for now,
58978025Sdes	 * so stop wasting our time
59073923Sjhb	 */
59178031Sdes	if (sc->sc_iferrs > LPMAXERRS) {
59278031Sdes	    printf("lp%d: Too many errors, Going off-line.\n", device_get_unit(dev));
59378031Sdes	    ppb_wctr(ppbus, 0x00);
59478031Sdes	    sc->sc_if.if_flags &= ~IFF_RUNNING;
59578031Sdes	    sc->sc_iferrs=0;
59678031Sdes	}
59778031Sdes
59878031Sdes    done:
59978031Sdes	splx(s);
60078031Sdes	return;
60178025Sdes}
60267588Sdes
60378031Sdesstatic __inline int
60471471Sjhblpoutbyte (u_char byte, int spin, device_t ppbus)
60578025Sdes{
606119068Sdes    ppb_wdtr(ppbus, txmith[byte]);
60767588Sdes    while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
60867588Sdes	if (--spin == 0)
60969799Sdes		return 1;
61069799Sdes    ppb_wdtr(ppbus, txmitl[byte]);
61169799Sdes    while (ppb_rstr(ppbus) & LPIP_SHAKE)
61269799Sdes	if (--spin == 0)
61369799Sdes		return 1;
61469799Sdes    return 0;
61569799Sdes}
61669799Sdes
61767588Sdesstatic int
618113574Sjhblpoutput (struct ifnet *ifp, struct mbuf *m,
61978025Sdes	  struct sockaddr *dst, struct rtentry *rt)
620113574Sjhb{
621113574Sjhb    device_t dev = UNITODEVICE(ifp->if_unit);
622113574Sjhb    device_t ppbus = device_get_parent(dev);
623113574Sjhb    int s, err;
62469995Sdes    struct mbuf *mm;
62569995Sdes    u_char *cp = "\0\0";
626113574Sjhb    u_char chksum = 0;
62767588Sdes    int count = 0;
62867588Sdes    int i, len, spin;
62967588Sdes
63067588Sdes    /* We need a sensible value if we abort */
63167588Sdes    cp++;
63267588Sdes    ifp->if_flags |= IFF_RUNNING;
63367588Sdes
63467588Sdes    err = 1;			/* assume we're aborting because of an error */
63567588Sdes
63667588Sdes    s = splhigh();
63767588Sdes
63867588Sdes    /* Suspend (on laptops) or receive-errors might have taken us offline */
63971471Sjhb    ppb_wctr(ppbus, IRQENABLE);
640104306Sjmallett
64169799Sdes    if (ifp->if_flags & IFF_LINK0) {
64269799Sdes
64369799Sdes	if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
64469799Sdes	    lprintf("&");
64578025Sdes	    lp_intr(dev);
646114983Sjhb	}
647114983Sjhb
648114983Sjhb	/* Alert other end to pending packet */
649114983Sjhb	spin = LPMAXSPIN1;
650114983Sjhb	ppb_wdtr(ppbus, 0x08);
65171471Sjhb	while ((ppb_rstr(ppbus) & 0x08) == 0)
652119068Sdes		if (--spin == 0) {
65367588Sdes			goto nend;
65467588Sdes		}
65567588Sdes
65667588Sdes	/* Calculate length of packet, then send that */
65767588Sdes
65878025Sdes	count += 14;		/* Ethernet header len */
65978025Sdes
66078025Sdes	mm = m;
661119068Sdes	for (mm = m; mm; mm = mm->m_next) {
66278025Sdes		count += mm->m_len;
66367588Sdes	}
66474135Sjlemon	if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
665119911Sdes		goto nend;
66678113Sdes	if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
667119911Sdes		goto nend;
668119911Sdes
669119911Sdes	/* Send dummy ethernet header */
670119911Sdes	for (i = 0; i < 12; i++) {
671119911Sdes		if (clpoutbyte(i, LPMAXSPIN1, ppbus))
672119911Sdes			goto nend;
673119911Sdes		chksum += i;
674119911Sdes	}
675119911Sdes
676119911Sdes	if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
677119911Sdes		goto nend;
678119911Sdes	if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
679119911Sdes		goto nend;
680119911Sdes	chksum += 0x08 + 0x00;		/* Add into checksum */
681119911Sdes
682119911Sdes	mm = m;
683119911Sdes	do {
684119911Sdes		cp = mtod(mm, u_char *);
685119911Sdes		len = mm->m_len;
686119911Sdes		while (len--) {
687119911Sdes			chksum += *cp;
688119911Sdes			if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
689119911Sdes				goto nend;
690119911Sdes		}
691119911Sdes	} while ((mm = mm->m_next));
692119911Sdes
693119911Sdes	/* Send checksum */
694119911Sdes	if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
695119911Sdes		goto nend;
696119911Sdes
697119911Sdes	/* Go quiescent */
698119911Sdes	ppb_wdtr(ppbus, 0);
699119911Sdes
700119911Sdes	err = 0;			/* No errors */
70178113Sdes
70278113Sdes	nend:
70378025Sdes	if (err)  {				/* if we didn't timeout... */
70478113Sdes		ifp->if_oerrors++;
70578113Sdes		lprintf("X");
70678113Sdes	} else {
707138281Scperciva		ifp->if_opackets++;
70878113Sdes		ifp->if_obytes += m->m_pkthdr.len;
70978113Sdes		if (ifp->if_bpf)
71078113Sdes		    lptap(ifp, m);
71178113Sdes	}
71278113Sdes
71378113Sdes	m_freem(m);
71478113Sdes
71578113Sdes	if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
71678113Sdes		lprintf("^");
71778113Sdes		lp_intr(dev);
71878113Sdes	}
71978113Sdes	(void) splx(s);
72094620Sjhb	return 0;
721127694Spjd    }
72294620Sjhb
72394620Sjhb    if (ppb_rstr(ppbus) & LPIP_SHAKE) {
72494620Sjhb        lprintf("&");
72594620Sjhb        lp_intr(dev);
72694620Sjhb    }
72794620Sjhb
72894620Sjhb    if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
729103767Sjake        goto end;
730103767Sjake    if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
73194620Sjhb        goto end;
73294620Sjhb
733138281Scperciva    mm = m;
734138281Scperciva    do {
735138281Scperciva        cp = mtod(mm,u_char *);
736138281Scperciva	len = mm->m_len;
737138281Scperciva        while (len--)
738138281Scperciva	    if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
739138281Scperciva	        goto end;
740138281Scperciva    } while ((mm = mm->m_next));
741138281Scperciva
742138281Scperciva    err = 0;				/* no errors were encountered */
74394620Sjhb
744138281Scperciva    end:
74594620Sjhb    --cp;
74678113Sdes    ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
747138281Scperciva
74878113Sdes    if (err)  {				/* if we didn't timeout... */
74978113Sdes	ifp->if_oerrors++;
75078113Sdes        lprintf("X");
75178113Sdes    } else {
75278113Sdes	ifp->if_opackets++;
75378113Sdes	ifp->if_obytes += m->m_pkthdr.len;
754116173Sobrien	if (ifp->if_bpf)
755116173Sobrien	    lptap(ifp, m);
756116173Sobrien    }
757116173Sobrien
758116173Sobrien    m_freem(m);
759116173Sobrien
760116173Sobrien    if (ppb_rstr(ppbus) & LPIP_SHAKE) {
761116173Sobrien	lprintf("^");
762116173Sobrien	lp_intr(dev);
763116173Sobrien    }
764116173Sobrien
765116173Sobrien    (void) splx(s);
766116173Sobrien    return 0;
767116173Sobrien}
768116173Sobrien
769116173Sobrienstatic device_method_t lp_methods[] = {
770121265Scognet  	/* device interface */
771121246Scognet	DEVMETHOD(device_identify,	lp_identify),
772121246Scognet	DEVMETHOD(device_probe,		lp_probe),
773121265Scognet	DEVMETHOD(device_attach,	lp_attach),
774121265Scognet
775121265Scognet	{ 0, 0 }
776121265Scognet};
777121265Scognet
778121265Scognetstatic driver_t lp_driver = {
779121265Scognet  "plip",
780137507Sphk  lp_methods,
781137507Sphk  sizeof(struct lp_data),
782121246Scognet};
783121246Scognet
784121246ScognetDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0);
785121246Scognet