if_plip.c revision 185003
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 185003 2008-11-16 17:42:02Z 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
81184896Sjhb */
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 */
113184896Sjhb#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
128185003Sjhb#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 */
130185003Sjhb#define	MLPIPHDRLEN	CLPIPHDRLEN
13138061Smsmith
132185003Sjhb#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
135185003Sjhb#define	MLPIPHDRLEN	LPIPHDRLEN
13638061Smsmith#endif
13738061Smsmith
13838061Smsmith#define	LPIPTBLSIZE	256	/* Size of octet translation table */
13938061Smsmith
140185003Sjhb#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;
150184130Sjhb	device_t	sc_dev;
15138061Smsmith	u_char		*sc_ifbuf;
15238061Smsmith	int		sc_iferrs;
15355939Snsouch
15455939Snsouch	struct resource *res_irq;
15538061Smsmith};
15638061Smsmith
15738061Smsmith/* Tables for the lp# interface */
15838061Smsmithstatic u_char *txmith;
159185003Sjhb#define	txmitl (txmith + (1 * LPIPTBLSIZE))
160185003Sjhb#define	trecvh (txmith + (2 * LPIPTBLSIZE))
161185003Sjhb#define	trecvl (txmith + (3 * LPIPTBLSIZE))
16238061Smsmith
16338061Smsmithstatic u_char *ctxmith;
164185003Sjhb#define	ctxmitl (ctxmith + (1 * LPIPTBLSIZE))
165185003Sjhb#define	ctrecvh (ctxmith + (2 * LPIPTBLSIZE))
166185003Sjhb#define	ctrecvl (ctxmith + (3 * LPIPTBLSIZE))
16738061Smsmith
16838061Smsmith/* Functions for the lp# interface */
16938061Smsmithstatic int lpinittables(void);
17038061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t);
17138061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
17238061Smsmith	struct rtentry *);
17355939Snsouchstatic void lp_intr(void *);
17438061Smsmith
175185003Sjhb#define	DEVTOSOFTC(dev) \
17655939Snsouch	((struct lp_data *)device_get_softc(dev))
17738061Smsmith
17855939Snsouchstatic devclass_t lp_devclass;
17955939Snsouch
18056455Speterstatic void
18156455Speterlp_identify(driver_t *driver, device_t parent)
18256455Speter{
183127189Sguido	device_t dev;
18455939Snsouch
185155606Sjhb	dev = device_find_child(parent, "plip", -1);
186127189Sguido	if (!dev)
187127189Sguido		BUS_ADD_CHILD(parent, 0, "plip", -1);
18856455Speter}
189184896Sjhb
19055939Snsouchstatic int
19155939Snsouchlp_probe(device_t dev)
19238061Smsmith{
19338061Smsmith
19455939Snsouch	device_set_desc(dev, "PLIP network interface");
19538061Smsmith
19655939Snsouch	return (0);
19738061Smsmith}
19838061Smsmith
19938061Smsmithstatic int
200184896Sjhblp_attach(device_t dev)
20138061Smsmith{
20255939Snsouch	struct lp_data *lp = DEVTOSOFTC(dev);
203147256Sbrooks	struct ifnet *ifp;
204183053Sjhb	int rid = 0;
20538061Smsmith
206184130Sjhb	lp->sc_dev = dev;
207184130Sjhb
208183053Sjhb	/*
209183053Sjhb	 * Reserve the interrupt resource.  If we don't have one, the
210183053Sjhb	 * attach fails.
211183053Sjhb	 */
212184896Sjhb	lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
213183053Sjhb	    RF_SHAREABLE);
214183053Sjhb	if (lp->res_irq == 0) {
215183053Sjhb		device_printf(dev, "cannot reserve interrupt, failed.\n");
216183053Sjhb		return (ENXIO);
217183053Sjhb	}
218183053Sjhb
219147256Sbrooks	ifp = lp->sc_ifp = if_alloc(IFT_PARA);
220147256Sbrooks	if (ifp == NULL) {
221147256Sbrooks		return (ENOSPC);
222147256Sbrooks	}
223147256Sbrooks
22455939Snsouch	ifp->if_softc = lp;
225121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
22638061Smsmith	ifp->if_mtu = LPMTU;
227133695Srwatson	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST |
228133695Srwatson	    IFF_NEEDSGIANT;
22938061Smsmith	ifp->if_ioctl = lpioctl;
23038061Smsmith	ifp->if_output = lpoutput;
23138061Smsmith	ifp->if_hdrlen = 0;
23238061Smsmith	ifp->if_addrlen = 0;
23338061Smsmith	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
23438061Smsmith	if_attach(ifp);
23538061Smsmith
23643773Sdes	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
23738061Smsmith
23855939Snsouch	return (0);
23938061Smsmith}
24038061Smsmith/*
24138061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol.
24238061Smsmith * We don't want to calculate these nasties in our tight loop, so we
24338061Smsmith * precalculate them when we initialize.
24438061Smsmith */
24538061Smsmithstatic int
246184896Sjhblpinittables(void)
24738061Smsmith{
248184896Sjhb	int i;
24938061Smsmith
250184896Sjhb	if (txmith == NULL)
251184896Sjhb		txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
25238061Smsmith
253184896Sjhb	if (txmith == NULL)
254184896Sjhb		return (1);
25538061Smsmith
256184896Sjhb	if (ctxmith == NULL)
257185003Sjhb		ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
25838061Smsmith
259184896Sjhb	if (ctxmith == NULL)
260184896Sjhb		return (1);
26138061Smsmith
262184896Sjhb	for (i = 0; i < LPIPTBLSIZE; i++) {
263184896Sjhb		ctxmith[i] = (i & 0xF0) >> 4;
264184896Sjhb		ctxmitl[i] = 0x10 | (i & 0x0F);
265184896Sjhb		ctrecvh[i] = (i & 0x78) << 1;
266184896Sjhb		ctrecvl[i] = (i & 0x78) >> 3;
267184896Sjhb	}
26838061Smsmith
269184896Sjhb	for (i = 0; i < LPIPTBLSIZE; i++) {
270184896Sjhb		txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
271184896Sjhb		txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
272184896Sjhb		trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
273184896Sjhb		trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
274184896Sjhb	}
27538061Smsmith
276184896Sjhb	return (0);
27738061Smsmith}
27838061Smsmith
27938061Smsmith/*
28038061Smsmith * Process an ioctl request.
28138061Smsmith */
28238061Smsmithstatic int
283184896Sjhblpioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
28438061Smsmith{
285184896Sjhb	struct lp_data *sc = ifp->if_softc;
286184896Sjhb	device_t dev = sc->sc_dev;
287184896Sjhb	device_t ppbus = device_get_parent(dev);
288184896Sjhb	struct ifaddr *ifa = (struct ifaddr *)data;
289184896Sjhb	struct ifreq *ifr = (struct ifreq *)data;
290184896Sjhb	u_char *ptr;
291184896Sjhb	void *ih;
292184896Sjhb	int error;
29338061Smsmith
294184896Sjhb	switch (cmd) {
295184896Sjhb	case SIOCSIFDSTADDR:
296184896Sjhb	case SIOCAIFADDR:
297184896Sjhb	case SIOCSIFADDR:
298184896Sjhb		if (ifa->ifa_addr->sa_family != AF_INET)
299184896Sjhb			return (EAFNOSUPPORT);
30038061Smsmith
301184896Sjhb		ifp->if_flags |= IFF_UP;
302184896Sjhb		/* FALLTHROUGH */
303184896Sjhb	case SIOCSIFFLAGS:
304184896Sjhb		if ((!(ifp->if_flags & IFF_UP)) &&
305184896Sjhb		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
30638061Smsmith
307184896Sjhb			ppb_wctr(ppbus, 0x00);
308184896Sjhb			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
30938061Smsmith
310184896Sjhb			/* IFF_UP is not set, try to release the bus anyway */
311184896Sjhb			ppb_release_bus(ppbus, dev);
312184896Sjhb			break;
313184896Sjhb		}
314184896Sjhb		if (((ifp->if_flags & IFF_UP)) &&
315184896Sjhb		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
31638061Smsmith
317184896Sjhb			/* XXX
318184896Sjhb			 * Should the request be interruptible?
319184896Sjhb			 */
320184896Sjhb			if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT |
321184896Sjhb			    PPB_INTR)))
322184896Sjhb				return (error);
32338061Smsmith
324184896Sjhb			/* Now IFF_UP means that we own the bus */
325184896Sjhb			ppb_set_mode(ppbus, PPB_COMPATIBLE);
32638061Smsmith
327184896Sjhb			if (lpinittables()) {
328184896Sjhb				ppb_release_bus(ppbus, dev);
329184896Sjhb				return (ENOBUFS);
330184896Sjhb			}
33142443Snsouch
332184896Sjhb			sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN,
333184896Sjhb			    M_DEVBUF, M_WAITOK);
334184896Sjhb			if (sc->sc_ifbuf == NULL) {
335184896Sjhb				ppb_release_bus(ppbus, dev);
336184896Sjhb				return (ENOBUFS);
337184896Sjhb			}
33842443Snsouch
339184896Sjhb			/*
340184896Sjhb			 * Attach our interrupt handler.  It is
341184896Sjhb			 * detached later when the bus is released.
342184896Sjhb			 */
343184896Sjhb			if ((error = bus_setup_intr(dev, sc->res_irq,
344184896Sjhb			    INTR_TYPE_NET, NULL, lp_intr, dev, &ih))) {
345184896Sjhb				ppb_release_bus(ppbus, dev);
346184896Sjhb				return (error);
347184896Sjhb			}
34838061Smsmith
349184896Sjhb			ppb_wctr(ppbus, IRQENABLE);
350184896Sjhb			ifp->if_drv_flags |= IFF_DRV_RUNNING;
351184896Sjhb		}
352184896Sjhb		break;
35338061Smsmith
354184896Sjhb	case SIOCSIFMTU:
355184896Sjhb		ptr = sc->sc_ifbuf;
356184896Sjhb		sc->sc_ifbuf = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF,
357184896Sjhb		    M_NOWAIT);
358184896Sjhb		if (sc->sc_ifbuf == NULL) {
359184896Sjhb			sc->sc_ifbuf = ptr;
360184896Sjhb			return (ENOBUFS);
361184896Sjhb		}
362184896Sjhb		if (ptr)
363184896Sjhb			free(ptr, M_DEVBUF);
364184896Sjhb		sc->sc_ifp->if_mtu = ifr->ifr_mtu;
365184896Sjhb		break;
36655939Snsouch
367184896Sjhb	case SIOCGIFMTU:
368184896Sjhb		ifr->ifr_mtu = sc->sc_ifp->if_mtu;
369184896Sjhb		break;
37038061Smsmith
371184896Sjhb	case SIOCADDMULTI:
372184896Sjhb	case SIOCDELMULTI:
373184896Sjhb		if (ifr == 0) {
374184896Sjhb			return (EAFNOSUPPORT);		/* XXX */
375184896Sjhb		}
376184896Sjhb		switch (ifr->ifr_addr.sa_family) {
377184896Sjhb		case AF_INET:
378184896Sjhb			break;
379184896Sjhb		default:
380184896Sjhb			return (EAFNOSUPPORT);
381184896Sjhb		}
382184896Sjhb		break;
38338061Smsmith
384184896Sjhb	case SIOCGIFMEDIA:
385184896Sjhb		/*
386184896Sjhb		 * No ifmedia support at this stage; maybe use it
387184896Sjhb		 * in future for eg. protocol selection.
388184896Sjhb		 */
389184896Sjhb		return (EINVAL);
39038061Smsmith
39138061Smsmith	default:
392184896Sjhb		lprintf("LP:ioctl(0x%lx)\n", cmd);
393184896Sjhb		return (EINVAL);
39438061Smsmith	}
395184896Sjhb	return (0);
39638061Smsmith}
39738061Smsmith
39838061Smsmithstatic __inline int
399184896Sjhbclpoutbyte(u_char byte, int spin, device_t ppbus)
40038061Smsmith{
401184896Sjhb
40255939Snsouch	ppb_wdtr(ppbus, ctxmitl[byte]);
40355939Snsouch	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
40438061Smsmith		if (--spin == 0) {
405184896Sjhb			return (1);
40638061Smsmith		}
40755939Snsouch	ppb_wdtr(ppbus, ctxmith[byte]);
40855939Snsouch	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
40938061Smsmith		if (--spin == 0) {
410184896Sjhb			return (1);
41138061Smsmith		}
412184896Sjhb	return (0);
41338061Smsmith}
41438061Smsmith
41538061Smsmithstatic __inline int
416184896Sjhbclpinbyte(int spin, device_t ppbus)
41738061Smsmith{
41842443Snsouch	u_char c, cl;
41938061Smsmith
42055939Snsouch	while((ppb_rstr(ppbus) & CLPIP_SHAKE))
421184896Sjhb		if (!--spin) {
422184896Sjhb			return (-1);
423184896Sjhb		}
42455939Snsouch	cl = ppb_rstr(ppbus);
42555939Snsouch	ppb_wdtr(ppbus, 0x10);
42638061Smsmith
42755939Snsouch	while(!(ppb_rstr(ppbus) & CLPIP_SHAKE))
428184896Sjhb		if (!--spin) {
429184896Sjhb			return (-1);
430184896Sjhb		}
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;
441184896Sjhb
442165640Sjhb	bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
44343773Sdes}
44443773Sdes
44543773Sdesstatic void
446184896Sjhblp_intr(void *arg)
44738061Smsmith{
44855939Snsouch	device_t dev = (device_t)arg;
449185003Sjhb	device_t ppbus = device_get_parent(dev);
45055939Snsouch	struct lp_data *sc = DEVTOSOFTC(dev);
45138061Smsmith	int len, s, j;
45238061Smsmith	u_char *bp;
45338061Smsmith	u_char c, cl;
45438061Smsmith	struct mbuf *top;
45538061Smsmith
45638061Smsmith	s = splhigh();
45738061Smsmith
458147256Sbrooks	if (sc->sc_ifp->if_flags & IFF_LINK0) {
45938061Smsmith
460184896Sjhb		/* Ack. the request */
461184896Sjhb		ppb_wdtr(ppbus, 0x01);
46238061Smsmith
463184896Sjhb		/* Get the packet length */
464184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
465184896Sjhb		if (j == -1)
466184896Sjhb			goto err;
467184896Sjhb		len = j;
468184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
469184896Sjhb		if (j == -1)
470184896Sjhb			goto err;
471184896Sjhb		len = len + (j << 8);
472184896Sjhb		if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN)
473184896Sjhb			goto err;
47438061Smsmith
475184896Sjhb		bp = sc->sc_ifbuf;
47638061Smsmith
477184896Sjhb		while (len--) {
478184896Sjhb			j = clpinbyte(LPMAXSPIN2, ppbus);
479184896Sjhb			if (j == -1) {
480184896Sjhb				goto err;
481184896Sjhb			}
482184896Sjhb			*bp++ = j;
483184896Sjhb		}
48438061Smsmith
485184896Sjhb		/* Get and ignore checksum */
486184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
487184896Sjhb		if (j == -1) {
488184896Sjhb			goto err;
489184896Sjhb		}
49038061Smsmith
491184896Sjhb		len = bp - sc->sc_ifbuf;
492184896Sjhb		if (len <= CLPIPHDRLEN)
493184896Sjhb			goto err;
494184896Sjhb
495184896Sjhb		sc->sc_iferrs = 0;
496184896Sjhb
497184896Sjhb		len -= CLPIPHDRLEN;
498184896Sjhb		sc->sc_ifp->if_ipackets++;
499184896Sjhb		sc->sc_ifp->if_ibytes += len;
500184896Sjhb		top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp,
501184896Sjhb		    0);
502184896Sjhb		if (top) {
503184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
504184896Sjhb				lptap(sc->sc_ifp, top);
505184896Sjhb
506184896Sjhb			/* mbuf is free'd on failure. */
507184896Sjhb			netisr_queue(NETISR_IP, top);
508184896Sjhb		}
509184896Sjhb		goto done;
51038061Smsmith	}
51155939Snsouch	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
512184896Sjhb		len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
513184896Sjhb		bp  = sc->sc_ifbuf;
514184896Sjhb		while (len--) {
51538061Smsmith
516184896Sjhb			cl = ppb_rstr(ppbus);
517184896Sjhb			ppb_wdtr(ppbus, 8);
51838061Smsmith
519184896Sjhb			j = LPMAXSPIN2;
520184896Sjhb			while((ppb_rstr(ppbus) & LPIP_SHAKE))
521184896Sjhb				if (!--j)
522184896Sjhb					goto err;
52338061Smsmith
524184896Sjhb			c = ppb_rstr(ppbus);
525184896Sjhb			ppb_wdtr(ppbus, 0);
52638061Smsmith
527184896Sjhb			*bp++= trecvh[cl] | trecvl[c];
52838061Smsmith
529184896Sjhb			j = LPMAXSPIN2;
530184896Sjhb			while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) {
531184896Sjhb				if (cl != c &&
532184896Sjhb				    (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
533184896Sjhb				    (c & 0xf8))
534184896Sjhb					goto end;
535184896Sjhb				if (!--j)
536184896Sjhb					goto err;
537184896Sjhb			}
53838061Smsmith		}
53938061Smsmith
54038061Smsmith	end:
541184896Sjhb		len = bp - sc->sc_ifbuf;
542184896Sjhb		if (len <= LPIPHDRLEN)
543184896Sjhb			goto err;
54438061Smsmith
545184896Sjhb		sc->sc_iferrs = 0;
54638061Smsmith
547184896Sjhb		len -= LPIPHDRLEN;
548184896Sjhb		sc->sc_ifp->if_ipackets++;
549184896Sjhb		sc->sc_ifp->if_ibytes += len;
550184896Sjhb		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp,
551184896Sjhb		    0);
552184896Sjhb		if (top) {
553184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
554184896Sjhb				lptap(sc->sc_ifp, top);
555184896Sjhb
556184896Sjhb			/* mbuf is free'd on failure. */
557184896Sjhb			netisr_queue(NETISR_IP, top);
558184896Sjhb		}
55938061Smsmith	}
56038061Smsmith	goto done;
56138061Smsmith
562185003Sjhberr:
56355939Snsouch	ppb_wdtr(ppbus, 0);
56438061Smsmith	lprintf("R");
565147256Sbrooks	sc->sc_ifp->if_ierrors++;
56638061Smsmith	sc->sc_iferrs++;
56738061Smsmith
56838061Smsmith	/*
56938061Smsmith	 * We are not able to send receive anything for now,
57038061Smsmith	 * so stop wasting our time
57138061Smsmith	 */
57238061Smsmith	if (sc->sc_iferrs > LPMAXERRS) {
573184896Sjhb		if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n");
574184896Sjhb		ppb_wctr(ppbus, 0x00);
575184896Sjhb		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
576184896Sjhb		sc->sc_iferrs = 0;
57738061Smsmith	}
57838061Smsmith
579185003Sjhbdone:
58038061Smsmith	splx(s);
58138061Smsmith}
58238061Smsmith
58338061Smsmithstatic __inline int
584185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus)
58538061Smsmith{
586184896Sjhb
587184896Sjhb	ppb_wdtr(ppbus, txmith[byte]);
588184896Sjhb	while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
589184896Sjhb		if (--spin == 0)
590184896Sjhb			return (1);
591184896Sjhb	ppb_wdtr(ppbus, txmitl[byte]);
592184896Sjhb	while (ppb_rstr(ppbus) & LPIP_SHAKE)
593184896Sjhb		if (--spin == 0)
594184896Sjhb			return (1);
595184896Sjhb	return (0);
59638061Smsmith}
59738061Smsmith
59838061Smsmithstatic int
599184896Sjhblpoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
600184896Sjhb    struct rtentry *rt)
60138061Smsmith{
602184896Sjhb	struct lp_data *sc = ifp->if_softc;
603184896Sjhb	device_t dev = sc->sc_dev;
604184896Sjhb	device_t ppbus = device_get_parent(dev);
605184896Sjhb	int s, err;
606184896Sjhb	struct mbuf *mm;
607184896Sjhb	u_char *cp = "\0\0";
608184896Sjhb	u_char chksum = 0;
609184896Sjhb	int count = 0;
610184896Sjhb	int i, len, spin;
61138061Smsmith
612184896Sjhb	/* We need a sensible value if we abort */
613184896Sjhb	cp++;
614184896Sjhb	ifp->if_drv_flags |= IFF_DRV_RUNNING;
61538061Smsmith
616184896Sjhb	err = 1;		/* assume we're aborting because of an error */
61738061Smsmith
618184896Sjhb	s = splhigh();
61938061Smsmith
620184896Sjhb	/* Suspend (on laptops) or receive-errors might have taken us offline */
621184896Sjhb	ppb_wctr(ppbus, IRQENABLE);
62238061Smsmith
623184896Sjhb	if (ifp->if_flags & IFF_LINK0) {
624184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
625184896Sjhb			lprintf("&");
626184896Sjhb			lp_intr(dev);
627184896Sjhb		}
62838061Smsmith
629184896Sjhb		/* Alert other end to pending packet */
630184896Sjhb		spin = LPMAXSPIN1;
631184896Sjhb		ppb_wdtr(ppbus, 0x08);
632184896Sjhb		while ((ppb_rstr(ppbus) & 0x08) == 0)
633184896Sjhb			if (--spin == 0) {
634184896Sjhb				goto nend;
635184896Sjhb			}
63638061Smsmith
637184896Sjhb		/* Calculate length of packet, then send that */
638184896Sjhb
639184896Sjhb		count += 14;		/* Ethernet header len */
640184896Sjhb
641184896Sjhb		mm = m;
642184896Sjhb		for (mm = m; mm; mm = mm->m_next) {
643184896Sjhb			count += mm->m_len;
644184896Sjhb		}
645184896Sjhb		if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
64638061Smsmith			goto nend;
647184896Sjhb		if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
648184896Sjhb			goto nend;
649184896Sjhb
650184896Sjhb		/* Send dummy ethernet header */
651184896Sjhb		for (i = 0; i < 12; i++) {
652184896Sjhb			if (clpoutbyte(i, LPMAXSPIN1, ppbus))
653184896Sjhb				goto nend;
654184896Sjhb			chksum += i;
65538061Smsmith		}
65638061Smsmith
657184896Sjhb		if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
658184896Sjhb			goto nend;
659184896Sjhb		if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
660184896Sjhb			goto nend;
661184896Sjhb		chksum += 0x08 + 0x00;		/* Add into checksum */
66238061Smsmith
663184896Sjhb		mm = m;
664184896Sjhb		do {
665184896Sjhb			cp = mtod(mm, u_char *);
666184896Sjhb			len = mm->m_len;
667184896Sjhb			while (len--) {
668184896Sjhb				chksum += *cp;
669184896Sjhb				if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
670184896Sjhb					goto nend;
671184896Sjhb			}
672184896Sjhb		} while ((mm = mm->m_next));
67338061Smsmith
674184896Sjhb		/* Send checksum */
675184896Sjhb		if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
676184896Sjhb			goto nend;
677184896Sjhb
678184896Sjhb		/* Go quiescent */
679184896Sjhb		ppb_wdtr(ppbus, 0);
680184896Sjhb
681184896Sjhb		err = 0;			/* No errors */
682184896Sjhb
683184896Sjhb	nend:
684184896Sjhb		if (err)  {			/* if we didn't timeout... */
685184896Sjhb			ifp->if_oerrors++;
686184896Sjhb			lprintf("X");
687184896Sjhb		} else {
688184896Sjhb			ifp->if_opackets++;
689184896Sjhb			ifp->if_obytes += m->m_pkthdr.len;
690184896Sjhb			if (bpf_peers_present(ifp->if_bpf))
691184896Sjhb				lptap(ifp, m);
692184896Sjhb		}
693184896Sjhb
694184896Sjhb		m_freem(m);
695184896Sjhb
696184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
697184896Sjhb			lprintf("^");
698184896Sjhb			lp_intr(dev);
699184896Sjhb		}
700184896Sjhb		(void) splx(s);
701184896Sjhb		return (0);
70238061Smsmith	}
70338061Smsmith
704184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
705184896Sjhb		lprintf("&");
706184896Sjhb		lp_intr(dev);
70738061Smsmith	}
70838061Smsmith
709184896Sjhb	if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
710184896Sjhb		goto end;
711184896Sjhb	if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
712184896Sjhb		goto end;
71338061Smsmith
71438061Smsmith	mm = m;
71538061Smsmith	do {
71638061Smsmith		cp = mtod(mm, u_char *);
71743773Sdes		len = mm->m_len;
718184896Sjhb		while (len--)
719184896Sjhb			if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
720184896Sjhb				goto end;
72138061Smsmith	} while ((mm = mm->m_next));
72238061Smsmith
723184896Sjhb	err = 0;			/* no errors were encountered */
72438061Smsmith
725184896Sjhbend:
726184896Sjhb	--cp;
727184896Sjhb	ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
72838061Smsmith
729184896Sjhb	if (err)  {			/* if we didn't timeout... */
73038061Smsmith		ifp->if_oerrors++;
73138061Smsmith		lprintf("X");
73238061Smsmith	} else {
73338061Smsmith		ifp->if_opackets++;
73438061Smsmith		ifp->if_obytes += m->m_pkthdr.len;
735165632Sjhb		if (bpf_peers_present(ifp->if_bpf))
736184896Sjhb			lptap(ifp, m);
73738061Smsmith	}
73838061Smsmith
73938061Smsmith	m_freem(m);
74038061Smsmith
741184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
74238061Smsmith		lprintf("^");
74355939Snsouch		lp_intr(dev);
74438061Smsmith	}
745184896Sjhb
74638061Smsmith	(void) splx(s);
747184896Sjhb	return (0);
74838061Smsmith}
74955939Snsouch
75056455Speterstatic device_method_t lp_methods[] = {
75156455Speter  	/* device interface */
75256455Speter	DEVMETHOD(device_identify,	lp_identify),
75356455Speter	DEVMETHOD(device_probe,		lp_probe),
75456455Speter	DEVMETHOD(device_attach,	lp_attach),
75556455Speter
75656455Speter	{ 0, 0 }
75756455Speter};
75856455Speter
75956455Speterstatic driver_t lp_driver = {
760184896Sjhb	"plip",
761184896Sjhb	lp_methods,
762184896Sjhb	sizeof(struct lp_data),
76356455Speter};
76456455Speter
76555939SnsouchDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, 0, 0);
766153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1);
767