if_plip.c revision 191148
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: head/sys/dev/ppbus/if_plip.c 191148 2009-04-16 20:30:28Z kmacy $");
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_types.h>
101#include <net/netisr.h>
102#include <net/route.h>
103
104#include <netinet/in.h>
105#include <netinet/in_var.h>
106
107#include <net/bpf.h>
108
109#include <dev/ppbus/ppbconf.h>
110#include "ppbus_if.h"
111#include <dev/ppbus/ppbio.h>
112
113#ifndef LPMTU			/* MTU for the lp# interfaces */
114#define	LPMTU		1500
115#endif
116
117#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
118#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
119#endif
120
121#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
122#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
123#endif
124
125#ifndef LPMAXERRS		/* Max errors before !RUNNING */
126#define	LPMAXERRS	100
127#endif
128
129#define	CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
130#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
131#define	MLPIPHDRLEN	CLPIPHDRLEN
132
133#define	LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
134#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
135#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
136#define	MLPIPHDRLEN	LPIPHDRLEN
137#endif
138
139#define	LPIPTBLSIZE	256	/* Size of octet translation table */
140
141#define	lprintf		if (lptflag) printf
142
143#ifdef PLIP_DEBUG
144static int volatile lptflag = 1;
145#else
146static int volatile lptflag = 0;
147#endif
148
149struct lp_data {
150	struct  ifnet	*sc_ifp;
151	device_t	sc_dev;
152	u_char		*sc_ifbuf;
153	int		sc_iferrs;
154
155	struct resource *res_irq;
156	void		*sc_intr_cookie;
157};
158
159static struct mtx lp_tables_lock;
160MTX_SYSINIT(lp_tables, &lp_tables_lock, "plip tables", MTX_DEF);
161
162/* Tables for the lp# interface */
163static u_char *txmith;
164#define	txmitl (txmith + (1 * LPIPTBLSIZE))
165#define	trecvh (txmith + (2 * LPIPTBLSIZE))
166#define	trecvl (txmith + (3 * LPIPTBLSIZE))
167
168static u_char *ctxmith;
169#define	ctxmitl (ctxmith + (1 * LPIPTBLSIZE))
170#define	ctrecvh (ctxmith + (2 * LPIPTBLSIZE))
171#define	ctrecvl (ctxmith + (3 * LPIPTBLSIZE))
172
173/* Functions for the lp# interface */
174static int lpinittables(void);
175static int lpioctl(struct ifnet *, u_long, caddr_t);
176static int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
177       struct route *);
178static void lpstop(struct lp_data *);
179static void lp_intr(void *);
180static int lp_module_handler(module_t, int, void *);
181
182#define	DEVTOSOFTC(dev) \
183	((struct lp_data *)device_get_softc(dev))
184
185static devclass_t lp_devclass;
186
187static int
188lp_module_handler(module_t mod, int what, void *arg)
189{
190
191	switch (what) {
192	case MOD_UNLOAD:
193		mtx_lock(&lp_tables_lock);
194		if (txmith != NULL) {
195			free(txmith, M_DEVBUF);
196			txmith = NULL;
197		}
198		if (ctxmith != NULL) {
199			free(ctxmith, M_DEVBUF);
200			ctxmith = NULL;
201		}
202		mtx_unlock(&lp_tables_lock);
203		break;
204	case MOD_LOAD:
205	case MOD_QUIESCE:
206		break;
207	default:
208		return (EOPNOTSUPP);
209	}
210	return (0);
211}
212
213static void
214lp_identify(driver_t *driver, device_t parent)
215{
216	device_t dev;
217
218	dev = device_find_child(parent, "plip", -1);
219	if (!dev)
220		BUS_ADD_CHILD(parent, 0, "plip", -1);
221}
222
223static int
224lp_probe(device_t dev)
225{
226
227	device_set_desc(dev, "PLIP network interface");
228
229	return (0);
230}
231
232static int
233lp_attach(device_t dev)
234{
235	struct lp_data *lp = DEVTOSOFTC(dev);
236	struct ifnet *ifp;
237	int error, rid = 0;
238
239	lp->sc_dev = dev;
240
241	/*
242	 * Reserve the interrupt resource.  If we don't have one, the
243	 * attach fails.
244	 */
245	lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
246	    RF_SHAREABLE);
247	if (lp->res_irq == 0) {
248		device_printf(dev, "cannot reserve interrupt, failed.\n");
249		return (ENXIO);
250	}
251
252	ifp = lp->sc_ifp = if_alloc(IFT_PARA);
253	if (ifp == NULL) {
254		return (ENOSPC);
255	}
256
257	ifp->if_softc = lp;
258	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
259	ifp->if_mtu = LPMTU;
260	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
261	ifp->if_ioctl = lpioctl;
262	ifp->if_output = lpoutput;
263	ifp->if_hdrlen = 0;
264	ifp->if_addrlen = 0;
265	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
266	if_attach(ifp);
267
268	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
269
270	/*
271	 * Attach our interrupt handler.  It is only called while we
272	 * own the ppbus.
273	 */
274	error = bus_setup_intr(dev, lp->res_irq, INTR_TYPE_NET | INTR_MPSAFE,
275	    NULL, lp_intr, lp, &lp->sc_intr_cookie);
276	if (error) {
277		bpfdetach(ifp);
278		if_detach(ifp);
279		bus_release_resource(dev, SYS_RES_IRQ, 0, lp->res_irq);
280		device_printf(dev, "Unable to register interrupt handler\n");
281		return (error);
282	}
283
284	return (0);
285}
286
287static int
288lp_detach(device_t dev)
289{
290	struct lp_data *sc = device_get_softc(dev);
291	device_t ppbus = device_get_parent(dev);
292
293	ppb_lock(ppbus);
294	lpstop(sc);
295	ppb_unlock(ppbus);
296	bpfdetach(sc->sc_ifp);
297	if_detach(sc->sc_ifp);
298	bus_teardown_intr(dev, sc->res_irq, sc->sc_intr_cookie);
299	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->res_irq);
300	return (0);
301}
302
303/*
304 * Build the translation tables for the LPIP (BSD unix) protocol.
305 * We don't want to calculate these nasties in our tight loop, so we
306 * precalculate them when we initialize.
307 */
308static int
309lpinittables(void)
310{
311	int i;
312
313	mtx_lock(&lp_tables_lock);
314	if (txmith == NULL)
315		txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
316
317	if (txmith == NULL) {
318		mtx_unlock(&lp_tables_lock);
319		return (1);
320	}
321
322	if (ctxmith == NULL)
323		ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
324
325	if (ctxmith == NULL) {
326		mtx_unlock(&lp_tables_lock);
327		return (1);
328	}
329
330	for (i = 0; i < LPIPTBLSIZE; i++) {
331		ctxmith[i] = (i & 0xF0) >> 4;
332		ctxmitl[i] = 0x10 | (i & 0x0F);
333		ctrecvh[i] = (i & 0x78) << 1;
334		ctrecvl[i] = (i & 0x78) >> 3;
335	}
336
337	for (i = 0; i < LPIPTBLSIZE; i++) {
338		txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
339		txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
340		trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
341		trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
342	}
343	mtx_unlock(&lp_tables_lock);
344
345	return (0);
346}
347
348static void
349lpstop(struct lp_data *sc)
350{
351	device_t ppbus = device_get_parent(sc->sc_dev);
352
353	ppb_assert_locked(ppbus);
354	ppb_wctr(ppbus, 0x00);
355	sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
356	free(sc->sc_ifbuf, M_DEVBUF);
357	sc->sc_ifbuf = NULL;
358
359	/* IFF_UP is not set, try to release the bus anyway */
360	ppb_release_bus(ppbus, sc->sc_dev);
361}
362
363static int
364lpinit_locked(struct ifnet *ifp)
365{
366	struct lp_data *sc = ifp->if_softc;
367	device_t dev = sc->sc_dev;
368	device_t ppbus = device_get_parent(dev);
369	int error;
370
371	ppb_assert_locked(ppbus);
372	error = ppb_request_bus(ppbus, dev, PPB_DONTWAIT);
373	if (error)
374		return (error);
375
376	/* Now IFF_UP means that we own the bus */
377	ppb_set_mode(ppbus, PPB_COMPATIBLE);
378
379	if (lpinittables()) {
380		ppb_release_bus(ppbus, dev);
381		return (ENOBUFS);
382	}
383
384	sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN,
385	    M_DEVBUF, M_NOWAIT);
386	if (sc->sc_ifbuf == NULL) {
387		ppb_release_bus(ppbus, dev);
388		return (ENOBUFS);
389	}
390
391	ppb_wctr(ppbus, IRQENABLE);
392
393	ifp->if_drv_flags |= IFF_DRV_RUNNING;
394	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
395	return (0);
396}
397
398/*
399 * Process an ioctl request.
400 */
401static int
402lpioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
403{
404	struct lp_data *sc = ifp->if_softc;
405	device_t dev = sc->sc_dev;
406	device_t ppbus = device_get_parent(dev);
407	struct ifaddr *ifa = (struct ifaddr *)data;
408	struct ifreq *ifr = (struct ifreq *)data;
409	u_char *ptr;
410	int error;
411
412	switch (cmd) {
413	case SIOCSIFDSTADDR:
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 == 0) {
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		sc->sc_ifp->if_ipackets++;
580		sc->sc_ifp->if_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			/* mbuf is free'd on failure. */
589			netisr_queue(NETISR_IP, top);
590			ppb_lock(ppbus);
591		}
592		return;
593	}
594	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
595		len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
596		bp  = sc->sc_ifbuf;
597		while (len--) {
598
599			cl = ppb_rstr(ppbus);
600			ppb_wdtr(ppbus, 8);
601
602			j = LPMAXSPIN2;
603			while ((ppb_rstr(ppbus) & LPIP_SHAKE))
604				if (!--j)
605					goto err;
606
607			c = ppb_rstr(ppbus);
608			ppb_wdtr(ppbus, 0);
609
610			*bp++= trecvh[cl] | trecvl[c];
611
612			j = LPMAXSPIN2;
613			while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) {
614				if (cl != c &&
615				    (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
616				    (c & 0xf8))
617					goto end;
618				if (!--j)
619					goto err;
620			}
621		}
622
623	end:
624		len = bp - sc->sc_ifbuf;
625		if (len <= LPIPHDRLEN)
626			goto err;
627
628		sc->sc_iferrs = 0;
629
630		len -= LPIPHDRLEN;
631		sc->sc_ifp->if_ipackets++;
632		sc->sc_ifp->if_ibytes += len;
633		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp,
634		    0);
635		if (top) {
636			ppb_unlock(ppbus);
637			if (bpf_peers_present(sc->sc_ifp->if_bpf))
638				lptap(sc->sc_ifp, top);
639
640			/* mbuf is free'd on failure. */
641			netisr_queue(NETISR_IP, top);
642			ppb_lock(ppbus);
643		}
644	}
645	return;
646
647err:
648	ppb_wdtr(ppbus, 0);
649	lprintf("R");
650	sc->sc_ifp->if_ierrors++;
651	sc->sc_iferrs++;
652
653	/*
654	 * We are not able to send receive anything for now,
655	 * so stop wasting our time
656	 */
657	if (sc->sc_iferrs > LPMAXERRS) {
658		if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n");
659		ppb_wctr(ppbus, 0x00);
660		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
661		sc->sc_iferrs = 0;
662	}
663}
664
665static __inline int
666lpoutbyte(u_char byte, int spin, device_t ppbus)
667{
668
669	ppb_wdtr(ppbus, txmith[byte]);
670	while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
671		if (--spin == 0)
672			return (1);
673	ppb_wdtr(ppbus, txmitl[byte]);
674	while (ppb_rstr(ppbus) & LPIP_SHAKE)
675		if (--spin == 0)
676			return (1);
677	return (0);
678}
679
680static int
681lpoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
682    struct route *ro)
683{
684	struct lp_data *sc = ifp->if_softc;
685	device_t dev = sc->sc_dev;
686	device_t ppbus = device_get_parent(dev);
687	int err;
688	struct mbuf *mm;
689	u_char *cp = "\0\0";
690	u_char chksum = 0;
691	int count = 0;
692	int i, len, spin;
693
694	/* We need a sensible value if we abort */
695	cp++;
696	ppb_lock(ppbus);
697	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
698
699	err = 1;		/* assume we're aborting because of an error */
700
701	/* Suspend (on laptops) or receive-errors might have taken us offline */
702	ppb_wctr(ppbus, IRQENABLE);
703
704	if (ifp->if_flags & IFF_LINK0) {
705		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
706			lprintf("&");
707			lp_intr(sc);
708		}
709
710		/* Alert other end to pending packet */
711		spin = LPMAXSPIN1;
712		ppb_wdtr(ppbus, 0x08);
713		while ((ppb_rstr(ppbus) & 0x08) == 0)
714			if (--spin == 0) {
715				goto nend;
716			}
717
718		/* Calculate length of packet, then send that */
719
720		count += 14;		/* Ethernet header len */
721
722		mm = m;
723		for (mm = m; mm; mm = mm->m_next) {
724			count += mm->m_len;
725		}
726		if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
727			goto nend;
728		if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
729			goto nend;
730
731		/* Send dummy ethernet header */
732		for (i = 0; i < 12; i++) {
733			if (clpoutbyte(i, LPMAXSPIN1, ppbus))
734				goto nend;
735			chksum += i;
736		}
737
738		if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
739			goto nend;
740		if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
741			goto nend;
742		chksum += 0x08 + 0x00;		/* Add into checksum */
743
744		mm = m;
745		do {
746			cp = mtod(mm, u_char *);
747			len = mm->m_len;
748			while (len--) {
749				chksum += *cp;
750				if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
751					goto nend;
752			}
753		} while ((mm = mm->m_next));
754
755		/* Send checksum */
756		if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
757			goto nend;
758
759		/* Go quiescent */
760		ppb_wdtr(ppbus, 0);
761
762		err = 0;			/* No errors */
763
764	nend:
765		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
766		if (err)  {			/* if we didn't timeout... */
767			ifp->if_oerrors++;
768			lprintf("X");
769		} else {
770			ifp->if_opackets++;
771			ifp->if_obytes += m->m_pkthdr.len;
772			if (bpf_peers_present(ifp->if_bpf))
773				lptap(ifp, m);
774		}
775
776		m_freem(m);
777
778		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
779			lprintf("^");
780			lp_intr(sc);
781		}
782		ppb_unlock(ppbus);
783		return (0);
784	}
785
786	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
787		lprintf("&");
788		lp_intr(sc);
789	}
790
791	if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
792		goto end;
793	if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
794		goto end;
795
796	mm = m;
797	do {
798		cp = mtod(mm, u_char *);
799		len = mm->m_len;
800		while (len--)
801			if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
802				goto end;
803	} while ((mm = mm->m_next));
804
805	err = 0;			/* no errors were encountered */
806
807end:
808	--cp;
809	ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
810
811	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
812	if (err)  {			/* if we didn't timeout... */
813		ifp->if_oerrors++;
814		lprintf("X");
815	} else {
816		ifp->if_opackets++;
817		ifp->if_obytes += m->m_pkthdr.len;
818		if (bpf_peers_present(ifp->if_bpf))
819			lptap(ifp, m);
820	}
821
822	m_freem(m);
823
824	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
825		lprintf("^");
826		lp_intr(sc);
827	}
828
829	ppb_unlock(ppbus);
830	return (0);
831}
832
833static device_method_t lp_methods[] = {
834  	/* device interface */
835	DEVMETHOD(device_identify,	lp_identify),
836	DEVMETHOD(device_probe,		lp_probe),
837	DEVMETHOD(device_attach,	lp_attach),
838	DEVMETHOD(device_detach,	lp_detach),
839
840	{ 0, 0 }
841};
842
843static driver_t lp_driver = {
844	"plip",
845	lp_methods,
846	sizeof(struct lp_data),
847};
848
849DRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0);
850MODULE_DEPEND(plip, ppbus, 1, 1, 1);
851