if_plip.c revision 331722
1/*-
2 * Copyright (c) 1997 Poul-Henning Kamp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/dev/ppbus/if_plip.c 331722 2018-03-29 02:50:57Z eadler $");
31
32/*
33 * Parallel port TCP/IP interfaces added.  I looked at the driver from
34 * MACH but this is a complete rewrite, and btw. incompatible, and it
35 * should perform better too.  I have never run the MACH driver though.
36 *
37 * This driver sends two bytes (0x08, 0x00) in front of each packet,
38 * to allow us to distinguish another format later.
39 *
40 * Now added a Linux/Crynwr compatibility mode which is enabled using
41 * IF_LINK0 - Tim Wilkinson.
42 *
43 * TODO:
44 *    Make HDLC/PPP mode, use IF_LLC1 to enable.
45 *
46 * Connect the two computers using a Laplink parallel cable to use this
47 * feature:
48 *
49 *      +----------------------------------------+
50 * 	|A-name	A-End	B-End	Descr.	Port/Bit |
51 *      +----------------------------------------+
52 *	|DATA0	2	15	Data	0/0x01   |
53 *	|-ERROR	15	2	   	1/0x08   |
54 *      +----------------------------------------+
55 *	|DATA1	3	13	Data	0/0x02	 |
56 *	|+SLCT	13	3	   	1/0x10   |
57 *      +----------------------------------------+
58 *	|DATA2	4	12	Data	0/0x04   |
59 *	|+PE	12	4	   	1/0x20   |
60 *      +----------------------------------------+
61 *	|DATA3	5	10	Strobe	0/0x08   |
62 *	|-ACK	10	5	   	1/0x40   |
63 *      +----------------------------------------+
64 *	|DATA4	6	11	Data	0/0x10   |
65 *	|BUSY	11	6	   	1/~0x80  |
66 *      +----------------------------------------+
67 *	|GND	18-25	18-25	GND	-        |
68 *      +----------------------------------------+
69 *
70 * Expect transfer-rates up to 75 kbyte/sec.
71 *
72 * If GCC could correctly grok
73 *	register int port asm("edx")
74 * the code would be cleaner
75 *
76 * Poul-Henning Kamp <phk@freebsd.org>
77 */
78
79/*
80 * Update for ppbus, PLIP support only - Nicolas Souchu
81 */
82
83#include "opt_plip.h"
84
85#include <sys/param.h>
86#include <sys/systm.h>
87#include <sys/module.h>
88#include <sys/bus.h>
89#include <sys/mbuf.h>
90#include <sys/socket.h>
91#include <sys/sockio.h>
92#include <sys/kernel.h>
93#include <sys/malloc.h>
94
95#include <machine/bus.h>
96#include <machine/resource.h>
97#include <sys/rman.h>
98
99#include <net/if.h>
100#include <net/if_var.h>
101#include <net/if_types.h>
102#include <net/netisr.h>
103#include <net/route.h>
104
105#include <netinet/in.h>
106#include <netinet/in_var.h>
107
108#include <net/bpf.h>
109
110#include <dev/ppbus/ppbconf.h>
111#include "ppbus_if.h"
112#include <dev/ppbus/ppbio.h>
113
114#ifndef LPMTU			/* MTU for the lp# interfaces */
115#define	LPMTU		1500
116#endif
117
118#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
119#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
120#endif
121
122#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
123#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
124#endif
125
126#ifndef LPMAXERRS		/* Max errors before !RUNNING */
127#define	LPMAXERRS	100
128#endif
129
130#define	CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
131#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
132#define	MLPIPHDRLEN	CLPIPHDRLEN
133
134#define	LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
135#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
136#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
137#define	MLPIPHDRLEN	LPIPHDRLEN
138#endif
139
140#define	LPIPTBLSIZE	256	/* Size of octet translation table */
141
142#define	lprintf		if (lptflag) printf
143
144#ifdef PLIP_DEBUG
145static int volatile lptflag = 1;
146#else
147static int volatile lptflag = 0;
148#endif
149
150struct lp_data {
151	struct  ifnet	*sc_ifp;
152	device_t	sc_dev;
153	u_char		*sc_ifbuf;
154	int		sc_iferrs;
155
156	struct resource *res_irq;
157	void		*sc_intr_cookie;
158};
159
160static struct mtx lp_tables_lock;
161MTX_SYSINIT(lp_tables, &lp_tables_lock, "plip tables", MTX_DEF);
162
163/* Tables for the lp# interface */
164static u_char *txmith;
165#define	txmitl (txmith + (1 * LPIPTBLSIZE))
166#define	trecvh (txmith + (2 * LPIPTBLSIZE))
167#define	trecvl (txmith + (3 * LPIPTBLSIZE))
168
169static u_char *ctxmith;
170#define	ctxmitl (ctxmith + (1 * LPIPTBLSIZE))
171#define	ctrecvh (ctxmith + (2 * LPIPTBLSIZE))
172#define	ctrecvl (ctxmith + (3 * LPIPTBLSIZE))
173
174/* Functions for the lp# interface */
175static int lpinittables(void);
176static int lpioctl(struct ifnet *, u_long, caddr_t);
177static int lpoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
178       struct route *);
179static void lpstop(struct lp_data *);
180static void lp_intr(void *);
181static int lp_module_handler(module_t, int, void *);
182
183#define	DEVTOSOFTC(dev) \
184	((struct lp_data *)device_get_softc(dev))
185
186static devclass_t lp_devclass;
187
188static int
189lp_module_handler(module_t mod, int what, void *arg)
190{
191
192	switch (what) {
193	case MOD_UNLOAD:
194		mtx_lock(&lp_tables_lock);
195		if (txmith != NULL) {
196			free(txmith, M_DEVBUF);
197			txmith = NULL;
198		}
199		if (ctxmith != NULL) {
200			free(ctxmith, M_DEVBUF);
201			ctxmith = NULL;
202		}
203		mtx_unlock(&lp_tables_lock);
204		break;
205	case MOD_LOAD:
206	case MOD_QUIESCE:
207		break;
208	default:
209		return (EOPNOTSUPP);
210	}
211	return (0);
212}
213
214static void
215lp_identify(driver_t *driver, device_t parent)
216{
217	device_t dev;
218
219	dev = device_find_child(parent, "plip", -1);
220	if (!dev)
221		BUS_ADD_CHILD(parent, 0, "plip", -1);
222}
223
224static int
225lp_probe(device_t dev)
226{
227
228	device_set_desc(dev, "PLIP network interface");
229
230	return (0);
231}
232
233static int
234lp_attach(device_t dev)
235{
236	struct lp_data *lp = DEVTOSOFTC(dev);
237	struct ifnet *ifp;
238	int error, rid = 0;
239
240	lp->sc_dev = dev;
241
242	/*
243	 * Reserve the interrupt resource.  If we don't have one, the
244	 * attach fails.
245	 */
246	lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
247	    RF_SHAREABLE);
248	if (lp->res_irq == NULL) {
249		device_printf(dev, "cannot reserve interrupt, failed.\n");
250		return (ENXIO);
251	}
252
253	ifp = lp->sc_ifp = if_alloc(IFT_PARA);
254	if (ifp == NULL) {
255		return (ENOSPC);
256	}
257
258	ifp->if_softc = lp;
259	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
260	ifp->if_mtu = LPMTU;
261	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
262	ifp->if_ioctl = lpioctl;
263	ifp->if_output = lpoutput;
264	ifp->if_hdrlen = 0;
265	ifp->if_addrlen = 0;
266	ifp->if_snd.ifq_maxlen = ifqmaxlen;
267	if_attach(ifp);
268
269	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
270
271	/*
272	 * Attach our interrupt handler.  It is only called while we
273	 * own the ppbus.
274	 */
275	error = bus_setup_intr(dev, lp->res_irq, INTR_TYPE_NET | INTR_MPSAFE,
276	    NULL, lp_intr, lp, &lp->sc_intr_cookie);
277	if (error) {
278		bpfdetach(ifp);
279		if_detach(ifp);
280		bus_release_resource(dev, SYS_RES_IRQ, 0, lp->res_irq);
281		device_printf(dev, "Unable to register interrupt handler\n");
282		return (error);
283	}
284
285	return (0);
286}
287
288static int
289lp_detach(device_t dev)
290{
291	struct lp_data *sc = device_get_softc(dev);
292	device_t ppbus = device_get_parent(dev);
293
294	ppb_lock(ppbus);
295	lpstop(sc);
296	ppb_unlock(ppbus);
297	bpfdetach(sc->sc_ifp);
298	if_detach(sc->sc_ifp);
299	bus_teardown_intr(dev, sc->res_irq, sc->sc_intr_cookie);
300	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->res_irq);
301	return (0);
302}
303
304/*
305 * Build the translation tables for the LPIP (BSD unix) protocol.
306 * We don't want to calculate these nasties in our tight loop, so we
307 * precalculate them when we initialize.
308 */
309static int
310lpinittables(void)
311{
312	int i;
313
314	mtx_lock(&lp_tables_lock);
315	if (txmith == NULL)
316		txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
317
318	if (txmith == NULL) {
319		mtx_unlock(&lp_tables_lock);
320		return (1);
321	}
322
323	if (ctxmith == NULL)
324		ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
325
326	if (ctxmith == NULL) {
327		mtx_unlock(&lp_tables_lock);
328		return (1);
329	}
330
331	for (i = 0; i < LPIPTBLSIZE; i++) {
332		ctxmith[i] = (i & 0xF0) >> 4;
333		ctxmitl[i] = 0x10 | (i & 0x0F);
334		ctrecvh[i] = (i & 0x78) << 1;
335		ctrecvl[i] = (i & 0x78) >> 3;
336	}
337
338	for (i = 0; i < LPIPTBLSIZE; i++) {
339		txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
340		txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
341		trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
342		trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
343	}
344	mtx_unlock(&lp_tables_lock);
345
346	return (0);
347}
348
349static void
350lpstop(struct lp_data *sc)
351{
352	device_t ppbus = device_get_parent(sc->sc_dev);
353
354	ppb_assert_locked(ppbus);
355	ppb_wctr(ppbus, 0x00);
356	sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
357	free(sc->sc_ifbuf, M_DEVBUF);
358	sc->sc_ifbuf = NULL;
359
360	/* IFF_UP is not set, try to release the bus anyway */
361	ppb_release_bus(ppbus, sc->sc_dev);
362}
363
364static int
365lpinit_locked(struct ifnet *ifp)
366{
367	struct lp_data *sc = ifp->if_softc;
368	device_t dev = sc->sc_dev;
369	device_t ppbus = device_get_parent(dev);
370	int error;
371
372	ppb_assert_locked(ppbus);
373	error = ppb_request_bus(ppbus, dev, PPB_DONTWAIT);
374	if (error)
375		return (error);
376
377	/* Now IFF_UP means that we own the bus */
378	ppb_set_mode(ppbus, PPB_COMPATIBLE);
379
380	if (lpinittables()) {
381		ppb_release_bus(ppbus, dev);
382		return (ENOBUFS);
383	}
384
385	sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN,
386	    M_DEVBUF, M_NOWAIT);
387	if (sc->sc_ifbuf == NULL) {
388		ppb_release_bus(ppbus, dev);
389		return (ENOBUFS);
390	}
391
392	ppb_wctr(ppbus, IRQENABLE);
393
394	ifp->if_drv_flags |= IFF_DRV_RUNNING;
395	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
396	return (0);
397}
398
399/*
400 * Process an ioctl request.
401 */
402static int
403lpioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
404{
405	struct lp_data *sc = ifp->if_softc;
406	device_t dev = sc->sc_dev;
407	device_t ppbus = device_get_parent(dev);
408	struct ifaddr *ifa = (struct ifaddr *)data;
409	struct ifreq *ifr = (struct ifreq *)data;
410	u_char *ptr;
411	int error;
412
413	switch (cmd) {
414	case SIOCAIFADDR:
415	case SIOCSIFADDR:
416		if (ifa->ifa_addr->sa_family != AF_INET)
417			return (EAFNOSUPPORT);
418
419		ifp->if_flags |= IFF_UP;
420		/* FALLTHROUGH */
421	case SIOCSIFFLAGS:
422		error = 0;
423		ppb_lock(ppbus);
424		if ((!(ifp->if_flags & IFF_UP)) &&
425		    (ifp->if_drv_flags & IFF_DRV_RUNNING))
426			lpstop(sc);
427		else if (((ifp->if_flags & IFF_UP)) &&
428		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING)))
429			error = lpinit_locked(ifp);
430		ppb_unlock(ppbus);
431		return (error);
432
433	case SIOCSIFMTU:
434		ppb_lock(ppbus);
435		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
436			ptr = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF,
437			    M_NOWAIT);
438			if (ptr == NULL) {
439				ppb_unlock(ppbus);
440				return (ENOBUFS);
441			}
442			if (sc->sc_ifbuf)
443				free(sc->sc_ifbuf, M_DEVBUF);
444			sc->sc_ifbuf = ptr;
445		}
446		sc->sc_ifp->if_mtu = ifr->ifr_mtu;
447		ppb_unlock(ppbus);
448		break;
449
450	case SIOCGIFMTU:
451		ifr->ifr_mtu = sc->sc_ifp->if_mtu;
452		break;
453
454	case SIOCADDMULTI:
455	case SIOCDELMULTI:
456		if (ifr == NULL) {
457			return (EAFNOSUPPORT);		/* XXX */
458		}
459		switch (ifr->ifr_addr.sa_family) {
460		case AF_INET:
461			break;
462		default:
463			return (EAFNOSUPPORT);
464		}
465		break;
466
467	case SIOCGIFMEDIA:
468		/*
469		 * No ifmedia support at this stage; maybe use it
470		 * in future for eg. protocol selection.
471		 */
472		return (EINVAL);
473
474	default:
475		lprintf("LP:ioctl(0x%lx)\n", cmd);
476		return (EINVAL);
477	}
478	return (0);
479}
480
481static __inline int
482clpoutbyte(u_char byte, int spin, device_t ppbus)
483{
484
485	ppb_wdtr(ppbus, ctxmitl[byte]);
486	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
487		if (--spin == 0) {
488			return (1);
489		}
490	ppb_wdtr(ppbus, ctxmith[byte]);
491	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
492		if (--spin == 0) {
493			return (1);
494		}
495	return (0);
496}
497
498static __inline int
499clpinbyte(int spin, device_t ppbus)
500{
501	u_char c, cl;
502
503	while ((ppb_rstr(ppbus) & CLPIP_SHAKE))
504		if (!--spin) {
505			return (-1);
506		}
507	cl = ppb_rstr(ppbus);
508	ppb_wdtr(ppbus, 0x10);
509
510	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
511		if (!--spin) {
512			return (-1);
513		}
514	c = ppb_rstr(ppbus);
515	ppb_wdtr(ppbus, 0x00);
516
517	return (ctrecvl[cl] | ctrecvh[c]);
518}
519
520static void
521lptap(struct ifnet *ifp, struct mbuf *m)
522{
523	u_int32_t af = AF_INET;
524
525	bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
526}
527
528static void
529lp_intr(void *arg)
530{
531	struct lp_data *sc = arg;
532	device_t ppbus = device_get_parent(sc->sc_dev);
533	int len, j;
534	u_char *bp;
535	u_char c, cl;
536	struct mbuf *top;
537
538	ppb_assert_locked(ppbus);
539	if (sc->sc_ifp->if_flags & IFF_LINK0) {
540
541		/* Ack. the request */
542		ppb_wdtr(ppbus, 0x01);
543
544		/* Get the packet length */
545		j = clpinbyte(LPMAXSPIN2, ppbus);
546		if (j == -1)
547			goto err;
548		len = j;
549		j = clpinbyte(LPMAXSPIN2, ppbus);
550		if (j == -1)
551			goto err;
552		len = len + (j << 8);
553		if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN)
554			goto err;
555
556		bp = sc->sc_ifbuf;
557
558		while (len--) {
559			j = clpinbyte(LPMAXSPIN2, ppbus);
560			if (j == -1) {
561				goto err;
562			}
563			*bp++ = j;
564		}
565
566		/* Get and ignore checksum */
567		j = clpinbyte(LPMAXSPIN2, ppbus);
568		if (j == -1) {
569			goto err;
570		}
571
572		len = bp - sc->sc_ifbuf;
573		if (len <= CLPIPHDRLEN)
574			goto err;
575
576		sc->sc_iferrs = 0;
577
578		len -= CLPIPHDRLEN;
579		if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
580		if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, len);
581		top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp,
582		    0);
583		if (top) {
584			ppb_unlock(ppbus);
585			if (bpf_peers_present(sc->sc_ifp->if_bpf))
586				lptap(sc->sc_ifp, top);
587
588			M_SETFIB(top, sc->sc_ifp->if_fib);
589
590			/* mbuf is free'd on failure. */
591			netisr_queue(NETISR_IP, top);
592			ppb_lock(ppbus);
593		}
594		return;
595	}
596	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
597		len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
598		bp  = sc->sc_ifbuf;
599		while (len--) {
600
601			cl = ppb_rstr(ppbus);
602			ppb_wdtr(ppbus, 8);
603
604			j = LPMAXSPIN2;
605			while ((ppb_rstr(ppbus) & LPIP_SHAKE))
606				if (!--j)
607					goto err;
608
609			c = ppb_rstr(ppbus);
610			ppb_wdtr(ppbus, 0);
611
612			*bp++= trecvh[cl] | trecvl[c];
613
614			j = LPMAXSPIN2;
615			while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) {
616				if (cl != c &&
617				    (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
618				    (c & 0xf8))
619					goto end;
620				if (!--j)
621					goto err;
622			}
623		}
624
625	end:
626		len = bp - sc->sc_ifbuf;
627		if (len <= LPIPHDRLEN)
628			goto err;
629
630		sc->sc_iferrs = 0;
631
632		len -= LPIPHDRLEN;
633		if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
634		if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, len);
635		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp,
636		    0);
637		if (top) {
638			ppb_unlock(ppbus);
639			if (bpf_peers_present(sc->sc_ifp->if_bpf))
640				lptap(sc->sc_ifp, top);
641
642			M_SETFIB(top, sc->sc_ifp->if_fib);
643
644			/* mbuf is free'd on failure. */
645			netisr_queue(NETISR_IP, top);
646			ppb_lock(ppbus);
647		}
648	}
649	return;
650
651err:
652	ppb_wdtr(ppbus, 0);
653	lprintf("R");
654	if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
655	sc->sc_iferrs++;
656
657	/*
658	 * We are not able to send receive anything for now,
659	 * so stop wasting our time
660	 */
661	if (sc->sc_iferrs > LPMAXERRS) {
662		if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n");
663		ppb_wctr(ppbus, 0x00);
664		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
665		sc->sc_iferrs = 0;
666	}
667}
668
669static __inline int
670lpoutbyte(u_char byte, int spin, device_t ppbus)
671{
672
673	ppb_wdtr(ppbus, txmith[byte]);
674	while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
675		if (--spin == 0)
676			return (1);
677	ppb_wdtr(ppbus, txmitl[byte]);
678	while (ppb_rstr(ppbus) & LPIP_SHAKE)
679		if (--spin == 0)
680			return (1);
681	return (0);
682}
683
684static int
685lpoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
686    struct route *ro)
687{
688	struct lp_data *sc = ifp->if_softc;
689	device_t dev = sc->sc_dev;
690	device_t ppbus = device_get_parent(dev);
691	int err;
692	struct mbuf *mm;
693	u_char *cp = "\0\0";
694	u_char chksum = 0;
695	int count = 0;
696	int i, len, spin;
697
698	/* We need a sensible value if we abort */
699	cp++;
700	ppb_lock(ppbus);
701	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
702
703	err = 1;		/* assume we're aborting because of an error */
704
705	/* Suspend (on laptops) or receive-errors might have taken us offline */
706	ppb_wctr(ppbus, IRQENABLE);
707
708	if (ifp->if_flags & IFF_LINK0) {
709		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
710			lprintf("&");
711			lp_intr(sc);
712		}
713
714		/* Alert other end to pending packet */
715		spin = LPMAXSPIN1;
716		ppb_wdtr(ppbus, 0x08);
717		while ((ppb_rstr(ppbus) & 0x08) == 0)
718			if (--spin == 0) {
719				goto nend;
720			}
721
722		/* Calculate length of packet, then send that */
723
724		count += 14;		/* Ethernet header len */
725
726		mm = m;
727		for (mm = m; mm; mm = mm->m_next) {
728			count += mm->m_len;
729		}
730		if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
731			goto nend;
732		if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
733			goto nend;
734
735		/* Send dummy ethernet header */
736		for (i = 0; i < 12; i++) {
737			if (clpoutbyte(i, LPMAXSPIN1, ppbus))
738				goto nend;
739			chksum += i;
740		}
741
742		if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
743			goto nend;
744		if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
745			goto nend;
746		chksum += 0x08 + 0x00;		/* Add into checksum */
747
748		mm = m;
749		do {
750			cp = mtod(mm, u_char *);
751			len = mm->m_len;
752			while (len--) {
753				chksum += *cp;
754				if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
755					goto nend;
756			}
757		} while ((mm = mm->m_next));
758
759		/* Send checksum */
760		if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
761			goto nend;
762
763		/* Go quiescent */
764		ppb_wdtr(ppbus, 0);
765
766		err = 0;			/* No errors */
767
768	nend:
769		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
770		if (err)  {			/* if we didn't timeout... */
771			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
772			lprintf("X");
773		} else {
774			if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
775			if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
776			if (bpf_peers_present(ifp->if_bpf))
777				lptap(ifp, m);
778		}
779
780		m_freem(m);
781
782		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
783			lprintf("^");
784			lp_intr(sc);
785		}
786		ppb_unlock(ppbus);
787		return (0);
788	}
789
790	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
791		lprintf("&");
792		lp_intr(sc);
793	}
794
795	if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
796		goto end;
797	if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
798		goto end;
799
800	mm = m;
801	do {
802		cp = mtod(mm, u_char *);
803		len = mm->m_len;
804		while (len--)
805			if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
806				goto end;
807	} while ((mm = mm->m_next));
808
809	err = 0;			/* no errors were encountered */
810
811end:
812	--cp;
813	ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
814
815	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
816	if (err)  {			/* if we didn't timeout... */
817		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
818		lprintf("X");
819	} else {
820		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
821		if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
822		if (bpf_peers_present(ifp->if_bpf))
823			lptap(ifp, m);
824	}
825
826	m_freem(m);
827
828	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
829		lprintf("^");
830		lp_intr(sc);
831	}
832
833	ppb_unlock(ppbus);
834	return (0);
835}
836
837static device_method_t lp_methods[] = {
838  	/* device interface */
839	DEVMETHOD(device_identify,	lp_identify),
840	DEVMETHOD(device_probe,		lp_probe),
841	DEVMETHOD(device_attach,	lp_attach),
842	DEVMETHOD(device_detach,	lp_detach),
843
844	{ 0, 0 }
845};
846
847static driver_t lp_driver = {
848	"plip",
849	lp_methods,
850	sizeof(struct lp_data),
851};
852
853DRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0);
854MODULE_DEPEND(plip, ppbus, 1, 1, 1);
855