xref: /netbsd-src/sys/dev/ppbus/if_plip.c (revision 2434a4afee45bb27c98b5dafb64e6396c9a66398)
1*2434a4afSrjs /* $NetBSD: if_plip.c,v 1.38 2022/09/04 15:59:08 rjs Exp $ */
2f7820334Sbjh21 
3e23cd1a7Sjdolecek /*-
4e23cd1a7Sjdolecek  * Copyright (c) 1997 Poul-Henning Kamp
563e9ee7bSjdolecek  * Copyright (c) 2003, 2004 Gary Thorpe <gathorpe@users.sourceforge.net>
6e23cd1a7Sjdolecek  * All rights reserved.
7e23cd1a7Sjdolecek  *
8e23cd1a7Sjdolecek  * Redistribution and use in source and binary forms, with or without
9e23cd1a7Sjdolecek  * modification, are permitted provided that the following conditions
10e23cd1a7Sjdolecek  * are met:
11e23cd1a7Sjdolecek  * 1. Redistributions of source code must retain the above copyright
12e23cd1a7Sjdolecek  *    notice, this list of conditions and the following disclaimer.
13e23cd1a7Sjdolecek  * 2. Redistributions in binary form must reproduce the above copyright
14e23cd1a7Sjdolecek  *    notice, this list of conditions and the following disclaimer in the
15e23cd1a7Sjdolecek  *    documentation and/or other materials provided with the distribution.
16e23cd1a7Sjdolecek  *
17e23cd1a7Sjdolecek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18e23cd1a7Sjdolecek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e23cd1a7Sjdolecek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e23cd1a7Sjdolecek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21e23cd1a7Sjdolecek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e23cd1a7Sjdolecek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e23cd1a7Sjdolecek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e23cd1a7Sjdolecek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e23cd1a7Sjdolecek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e23cd1a7Sjdolecek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e23cd1a7Sjdolecek  * SUCH DAMAGE.
28e23cd1a7Sjdolecek  *
29e23cd1a7Sjdolecek  *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
301c34707aSbjh21  * FreeBSD: src/sys/dev/ppbus/if_plip.c,v 1.19.2.1 2000/05/24 00:20:57 n_hibma Exp
31e23cd1a7Sjdolecek  */
32e23cd1a7Sjdolecek 
331c34707aSbjh21 #include <sys/cdefs.h>
34*2434a4afSrjs __KERNEL_RCSID(0, "$NetBSD: if_plip.c,v 1.38 2022/09/04 15:59:08 rjs Exp $");
351c34707aSbjh21 
36e23cd1a7Sjdolecek /*
37e23cd1a7Sjdolecek  * Parallel port TCP/IP interfaces added.  I looked at the driver from
38e23cd1a7Sjdolecek  * MACH but this is a complete rewrite, and btw. incompatible, and it
39e23cd1a7Sjdolecek  * should perform better too.  I have never run the MACH driver though.
40e23cd1a7Sjdolecek  *
41e23cd1a7Sjdolecek  * This driver sends two bytes (0x08, 0x00) in front of each packet,
42e23cd1a7Sjdolecek  * to allow us to distinguish another format later.
43e23cd1a7Sjdolecek  *
443d3638f6Smsaitoh  * Now added a Linux/Crynwr compatibility mode which is enabled using
45e23cd1a7Sjdolecek  * IF_LINK0 - Tim Wilkinson.
46e23cd1a7Sjdolecek  *
47e23cd1a7Sjdolecek  * TODO:
48e23cd1a7Sjdolecek  *    Make HDLC/PPP mode, use IF_LLC1 to enable.
49e23cd1a7Sjdolecek  *
50e23cd1a7Sjdolecek  * Connect the two computers using a Laplink parallel cable to use this
51e23cd1a7Sjdolecek  * feature:
52e23cd1a7Sjdolecek  *
53e23cd1a7Sjdolecek  *      +----------------------------------------+
54e23cd1a7Sjdolecek  * 	|A-name	A-End	B-End	Descr.	Port/Bit |
55e23cd1a7Sjdolecek  *      +----------------------------------------+
56e23cd1a7Sjdolecek  *	|DATA0	2	15	Data	0/0x01   |
57e23cd1a7Sjdolecek  *	|-ERROR	15	2	   	1/0x08   |
58e23cd1a7Sjdolecek  *      +----------------------------------------+
59e23cd1a7Sjdolecek  *	|DATA1	3	13	Data	0/0x02	 |
60e23cd1a7Sjdolecek  *	|+SLCT	13	3	   	1/0x10   |
61e23cd1a7Sjdolecek  *      +----------------------------------------+
62e23cd1a7Sjdolecek  *	|DATA2	4	12	Data	0/0x04   |
63e23cd1a7Sjdolecek  *	|+PE	12	4	   	1/0x20   |
64e23cd1a7Sjdolecek  *      +----------------------------------------+
65e23cd1a7Sjdolecek  *	|DATA3	5	10	Strobe	0/0x08   |
66e23cd1a7Sjdolecek  *	|-ACK	10	5	   	1/0x40   |
67e23cd1a7Sjdolecek  *      +----------------------------------------+
68e23cd1a7Sjdolecek  *	|DATA4	6	11	Data	0/0x10   |
69e23cd1a7Sjdolecek  *	|BUSY	11	6	   	1/~0x80  |
70e23cd1a7Sjdolecek  *      +----------------------------------------+
71e23cd1a7Sjdolecek  *	|GND	18-25	18-25	GND	-        |
72e23cd1a7Sjdolecek  *      +----------------------------------------+
73e23cd1a7Sjdolecek  *
74e23cd1a7Sjdolecek  * Expect transfer-rates up to 75 kbyte/sec.
75e23cd1a7Sjdolecek  *
76e23cd1a7Sjdolecek  * If GCC could correctly grok
7700d6acb4Sperry  *	register int port __asm("edx")
78e23cd1a7Sjdolecek  * the code would be cleaner
79e23cd1a7Sjdolecek  *
80e23cd1a7Sjdolecek  * Poul-Henning Kamp <phk@freebsd.org>
81e23cd1a7Sjdolecek  */
82e23cd1a7Sjdolecek 
83e23cd1a7Sjdolecek /*
84e23cd1a7Sjdolecek  * Update for ppbus, PLIP support only - Nicolas Souchu
85e23cd1a7Sjdolecek  */
86e23cd1a7Sjdolecek 
87e23cd1a7Sjdolecek #include "opt_inet.h"
88e23cd1a7Sjdolecek #include "opt_plip.h"
89e23cd1a7Sjdolecek 
90e23cd1a7Sjdolecek #include <sys/systm.h>
91e23cd1a7Sjdolecek #include <sys/param.h>
92e23cd1a7Sjdolecek #include <sys/proc.h>
93e23cd1a7Sjdolecek #include <sys/types.h>
94e23cd1a7Sjdolecek #include <sys/device.h>
95e23cd1a7Sjdolecek #include <sys/ioctl.h>
96e23cd1a7Sjdolecek #include <sys/malloc.h>
97e23cd1a7Sjdolecek #include <sys/mbuf.h>
98e23cd1a7Sjdolecek 
99e23cd1a7Sjdolecek #include <net/if.h>
100e23cd1a7Sjdolecek #include <net/if_types.h>
101e23cd1a7Sjdolecek 
102e23cd1a7Sjdolecek #include <sys/time.h>
103e23cd1a7Sjdolecek #include <net/bpf.h>
104e23cd1a7Sjdolecek 
105e23cd1a7Sjdolecek #ifdef INET
106*2434a4afSrjs #include <netinet/in.h>
107*2434a4afSrjs #include <netinet/in_systm.h>
108e23cd1a7Sjdolecek #include <netinet/in_var.h>
109*2434a4afSrjs #include <netinet/ip.h>
110e23cd1a7Sjdolecek #else
111e23cd1a7Sjdolecek #error Cannot config lp/plip without inet
112e23cd1a7Sjdolecek #endif
113e23cd1a7Sjdolecek 
114e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_base.h>
115e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_device.h>
116e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_io.h>
117e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_var.h>
118e23cd1a7Sjdolecek 
119e23cd1a7Sjdolecek #include <machine/types.h>
120a2a38285Sad #include <sys/intr.h>
121e23cd1a7Sjdolecek 
122e23cd1a7Sjdolecek #ifndef LPMTU			/* MTU for the lp# interfaces */
123e23cd1a7Sjdolecek #define	LPMTU		1500
124e23cd1a7Sjdolecek #endif
125e23cd1a7Sjdolecek 
126e23cd1a7Sjdolecek #ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
127e23cd1a7Sjdolecek #define	LPMAXSPIN1	8000	/* Spinning for remote intr to happen */
128e23cd1a7Sjdolecek #endif
129e23cd1a7Sjdolecek 
130e23cd1a7Sjdolecek #ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
131e23cd1a7Sjdolecek #define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
132e23cd1a7Sjdolecek #endif
133e23cd1a7Sjdolecek 
134e23cd1a7Sjdolecek #ifndef LPMAXERRS		/* Max errors before !RUNNING */
135e23cd1a7Sjdolecek #define	LPMAXERRS	100
136e23cd1a7Sjdolecek #endif
137e23cd1a7Sjdolecek 
138e23cd1a7Sjdolecek #ifndef LPMAXRTRY
139e23cd1a7Sjdolecek #define LPMAXRTRY	100	/* If channel busy, retry LPMAXRTRY
140e23cd1a7Sjdolecek 					consecutive times */
141e23cd1a7Sjdolecek #endif
142e23cd1a7Sjdolecek 
143e23cd1a7Sjdolecek #define CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
144e23cd1a7Sjdolecek #define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
145e23cd1a7Sjdolecek #define MLPIPHDRLEN	CLPIPHDRLEN
146e23cd1a7Sjdolecek 
147e23cd1a7Sjdolecek #define LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
148e23cd1a7Sjdolecek #define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
149e23cd1a7Sjdolecek #if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
150e23cd1a7Sjdolecek #define MLPIPHDRLEN	LPIPHDRLEN
151e23cd1a7Sjdolecek #endif
152e23cd1a7Sjdolecek 
153e23cd1a7Sjdolecek #define	LPIPTBLSIZE	256	/* Size of octet translation table */
154e23cd1a7Sjdolecek 
155e23cd1a7Sjdolecek #define LP_PRINTF	if (lpflag) printf
156e23cd1a7Sjdolecek 
157e23cd1a7Sjdolecek #ifdef PLIP_DEBUG
158e23cd1a7Sjdolecek static int volatile lpflag = 1;
159e23cd1a7Sjdolecek #else
160e23cd1a7Sjdolecek static int volatile lpflag = 0;
161e23cd1a7Sjdolecek #endif
162e23cd1a7Sjdolecek 
163e23cd1a7Sjdolecek /* Tx/Rsv tables for the lp interface */
164e23cd1a7Sjdolecek static u_char *txmith;
165e23cd1a7Sjdolecek #define txmitl (txmith+(1*LPIPTBLSIZE))
166e23cd1a7Sjdolecek #define trecvh (txmith+(2*LPIPTBLSIZE))
167e23cd1a7Sjdolecek #define trecvl (txmith+(3*LPIPTBLSIZE))
168e23cd1a7Sjdolecek static u_char *ctxmith;
169e23cd1a7Sjdolecek #define ctxmitl (ctxmith+(1*LPIPTBLSIZE))
170e23cd1a7Sjdolecek #define ctrecvh (ctxmith+(2*LPIPTBLSIZE))
171e23cd1a7Sjdolecek #define ctrecvl (ctxmith+(3*LPIPTBLSIZE))
172e23cd1a7Sjdolecek static uint16_t lp_count = 0;
173e23cd1a7Sjdolecek 
174e23cd1a7Sjdolecek /* Autoconf functions */
175b849cd90Scegger static int lp_probe(device_t, cfdata_t, void *);
176b849cd90Scegger static void lp_attach(device_t, device_t, void *);
177b849cd90Scegger static int lp_detach(device_t, int);
178e23cd1a7Sjdolecek 
179e23cd1a7Sjdolecek /* Soft config data */
180e23cd1a7Sjdolecek struct lp_softc {
181e23cd1a7Sjdolecek 	struct ppbus_device_softc ppbus_dev;
182e23cd1a7Sjdolecek 	struct ifnet sc_if;
183e23cd1a7Sjdolecek 	u_char *sc_ifbuf;
184e23cd1a7Sjdolecek 	unsigned short sc_iferrs;
185e23cd1a7Sjdolecek 	unsigned short sc_xmit_rtry;
186e23cd1a7Sjdolecek 	u_int8_t sc_dev_ok; /* Zero means ok */
187e23cd1a7Sjdolecek };
188e23cd1a7Sjdolecek 
189e23cd1a7Sjdolecek /* Autoconf structure */
190b849cd90Scegger CFATTACH_DECL_NEW(plip, sizeof(struct lp_softc), lp_probe, lp_attach, lp_detach,
191e23cd1a7Sjdolecek 	NULL);
192e23cd1a7Sjdolecek 
193e23cd1a7Sjdolecek /* Functions for the lp interface */
194e23cd1a7Sjdolecek static void lpinittables(void);
195e23cd1a7Sjdolecek static void lpfreetables(void);
19653524e44Schristos static int lpioctl(struct ifnet *, u_long, void *);
197bac28a1dSdrochner static int lpoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
19852e1f28cSchristos     const struct rtentry *);
199e23cd1a7Sjdolecek static void lpstart(struct ifnet *);
200e23cd1a7Sjdolecek static void lp_intr(void *);
201e23cd1a7Sjdolecek 
202e23cd1a7Sjdolecek 
203e23cd1a7Sjdolecek static int
lp_probe(device_t parent,cfdata_t match,void * aux)204b849cd90Scegger lp_probe(device_t parent, cfdata_t match, void *aux)
205e23cd1a7Sjdolecek {
206e23cd1a7Sjdolecek 	struct ppbus_attach_args * args = aux;
207e23cd1a7Sjdolecek 
208e23cd1a7Sjdolecek 	/* Fail if ppbus is not interrupt capable */
209e23cd1a7Sjdolecek 	if (args->capabilities & PPBUS_HAS_INTR)
210e23cd1a7Sjdolecek 		return 1;
211e23cd1a7Sjdolecek 
212e23cd1a7Sjdolecek 	printf("%s(%s): not an interrupt-driven port.\n", __func__,
2131b044f41Scegger 		device_xname(parent));
214e23cd1a7Sjdolecek 	return 0;
215e23cd1a7Sjdolecek }
216e23cd1a7Sjdolecek 
217e23cd1a7Sjdolecek static void
lp_attach(device_t parent,device_t self,void * aux)218b849cd90Scegger lp_attach(device_t parent, device_t self, void *aux)
219e23cd1a7Sjdolecek {
220ec03de0cSthorpej 	struct lp_softc * lp = device_private(self);
221e23cd1a7Sjdolecek 	struct ifnet * ifp = &lp->sc_if;
222e23cd1a7Sjdolecek 
223376411d2Scegger 	lp->ppbus_dev.sc_dev = self;
224e23cd1a7Sjdolecek 	lp->sc_dev_ok = 0;
225e23cd1a7Sjdolecek 	lp->sc_ifbuf = NULL;
226e23cd1a7Sjdolecek 	lp->sc_iferrs = 0;
227e23cd1a7Sjdolecek 	lp->sc_xmit_rtry = 0;
228e23cd1a7Sjdolecek 
229ec03de0cSthorpej 	ifp->if_softc = lp;
2301b044f41Scegger 	strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
231e23cd1a7Sjdolecek 	ifp->if_mtu = LPMTU;
232e23cd1a7Sjdolecek 	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
233e23cd1a7Sjdolecek 	ifp->if_ioctl = lpioctl;
234e23cd1a7Sjdolecek 	ifp->if_output = lpoutput;
235e23cd1a7Sjdolecek 	ifp->if_start = lpstart;
236e23cd1a7Sjdolecek 	ifp->if_type = IFT_PARA;
237e23cd1a7Sjdolecek 	ifp->if_hdrlen = 0;
238e23cd1a7Sjdolecek 	ifp->if_addrlen = 0;
239e23cd1a7Sjdolecek 	ifp->if_dlt = DLT_NULL;
240e23cd1a7Sjdolecek 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
241e23cd1a7Sjdolecek 	IFQ_SET_READY(&ifp->if_snd);
242e23cd1a7Sjdolecek 	if_attach(ifp);
243e23cd1a7Sjdolecek 	if_alloc_sadl(ifp);
244e23cd1a7Sjdolecek 
24558e86755Sjoerg 	bpf_attach(ifp, DLT_NULL, sizeof(u_int32_t));
246e23cd1a7Sjdolecek 
247e23cd1a7Sjdolecek 	if (lp_count++ == 0)
248e23cd1a7Sjdolecek 		lpinittables();
249e23cd1a7Sjdolecek 	printf("\n");
250e23cd1a7Sjdolecek }
251e23cd1a7Sjdolecek 
252e23cd1a7Sjdolecek static int
lp_detach(device_t self,int flags)253b849cd90Scegger lp_detach(device_t self, int flags)
254e23cd1a7Sjdolecek {
255e23cd1a7Sjdolecek 	int error = 0;
256ec03de0cSthorpej 	struct lp_softc * lp = device_private(self);
257b849cd90Scegger 	device_t ppbus = device_parent(self);
258e23cd1a7Sjdolecek 
259e23cd1a7Sjdolecek 	if (lp->sc_dev_ok) {
260e23cd1a7Sjdolecek 		if (!(flags & DETACH_QUIET))
261e23cd1a7Sjdolecek 			LP_PRINTF("%s(%s): device not properly attached! "
262e23cd1a7Sjdolecek 				"Skipping detach....\n", __func__,
2631b044f41Scegger 				device_xname(self));
264e23cd1a7Sjdolecek 		return error;
265e23cd1a7Sjdolecek 	}
266e23cd1a7Sjdolecek 
267e23cd1a7Sjdolecek 	/* If interface is up, bring it down and release ppbus */
268e23cd1a7Sjdolecek 	if (lp->sc_if.if_flags & IFF_RUNNING) {
269e23cd1a7Sjdolecek 		ppbus_wctr(ppbus, 0x00);
270e23cd1a7Sjdolecek 		if_detach(&lp->sc_if);
271e23cd1a7Sjdolecek 		error = ppbus_remove_handler(ppbus, lp_intr);
272e23cd1a7Sjdolecek 		if (error) {
273e23cd1a7Sjdolecek 			if (!(flags & DETACH_QUIET))
274e23cd1a7Sjdolecek 				LP_PRINTF("%s(%s): unable to remove interrupt "
275e23cd1a7Sjdolecek 					"callback.\n", __func__,
2761b044f41Scegger 					device_xname(self));
277e23cd1a7Sjdolecek 			if (!(flags & DETACH_FORCE))
278e23cd1a7Sjdolecek 				return error;
279e23cd1a7Sjdolecek 		}
280e23cd1a7Sjdolecek 		error = ppbus_release_bus(ppbus, self, 0, 0);
281e23cd1a7Sjdolecek 		if (error) {
282e23cd1a7Sjdolecek 			if (!(flags & DETACH_QUIET))
283e23cd1a7Sjdolecek 				LP_PRINTF("%s(%s): error releasing bus %s.\n",
2841b044f41Scegger 					__func__, device_xname(self),
2851b044f41Scegger 					device_xname(ppbus));
286e23cd1a7Sjdolecek 			if (!(flags & DETACH_FORCE))
287e23cd1a7Sjdolecek 				return error;
288e23cd1a7Sjdolecek 		}
289e23cd1a7Sjdolecek 	}
290e23cd1a7Sjdolecek 
291e23cd1a7Sjdolecek 	if (lp->sc_ifbuf)
292e23cd1a7Sjdolecek 		free(lp->sc_ifbuf, M_DEVBUF);
293e23cd1a7Sjdolecek 
294e23cd1a7Sjdolecek 	if (--lp_count == 0)
295e23cd1a7Sjdolecek 		lpfreetables();
296e23cd1a7Sjdolecek 	return error;
297e23cd1a7Sjdolecek }
298e23cd1a7Sjdolecek 
299e23cd1a7Sjdolecek /*
300e23cd1a7Sjdolecek  * Build the translation tables for the LPIP (BSD unix) protocol.
301e23cd1a7Sjdolecek  * We don't want to calculate these nasties in our tight loop, so we
302e23cd1a7Sjdolecek  * precalculate them when we initialize.
303e23cd1a7Sjdolecek  */
304e23cd1a7Sjdolecek static void
lpinittables(void)305e23cd1a7Sjdolecek lpinittables(void)
306e23cd1a7Sjdolecek {
307e23cd1a7Sjdolecek 	int i;
308e23cd1a7Sjdolecek 
309e23cd1a7Sjdolecek 	if (!txmith)
310e23cd1a7Sjdolecek 		txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_WAITOK);
311e23cd1a7Sjdolecek 
312e23cd1a7Sjdolecek 	if (!ctxmith)
313e23cd1a7Sjdolecek 		ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_WAITOK);
314e23cd1a7Sjdolecek 
315e23cd1a7Sjdolecek 	for (i = 0; i < LPIPTBLSIZE; i++) {
316e23cd1a7Sjdolecek 		ctxmith[i] = (i & 0xF0) >> 4;
317e23cd1a7Sjdolecek 		ctxmitl[i] = 0x10 | (i & 0x0F);
318e23cd1a7Sjdolecek 		ctrecvh[i] = (i & 0x78) << 1;
319e23cd1a7Sjdolecek 		ctrecvl[i] = (i & 0x78) >> 3;
320e23cd1a7Sjdolecek 	}
321e23cd1a7Sjdolecek 
322e23cd1a7Sjdolecek 	for (i = 0; i < LPIPTBLSIZE; i++) {
323e23cd1a7Sjdolecek 		txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
324e23cd1a7Sjdolecek 		txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
325e23cd1a7Sjdolecek 		trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
326e23cd1a7Sjdolecek 		trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
327e23cd1a7Sjdolecek 	}
328e23cd1a7Sjdolecek }
329e23cd1a7Sjdolecek 
330e23cd1a7Sjdolecek /* Free translation tables */
331e23cd1a7Sjdolecek static void
lpfreetables(void)332e23cd1a7Sjdolecek lpfreetables(void)
333e23cd1a7Sjdolecek {
334e23cd1a7Sjdolecek 	if (txmith)
335e23cd1a7Sjdolecek 		free(txmith, M_DEVBUF);
336e23cd1a7Sjdolecek 	if (ctxmith)
337e23cd1a7Sjdolecek 		free(ctxmith, M_DEVBUF);
338e23cd1a7Sjdolecek 	txmith = ctxmith = NULL;
339e23cd1a7Sjdolecek }
340e23cd1a7Sjdolecek 
341e23cd1a7Sjdolecek 
342e23cd1a7Sjdolecek /* Process an ioctl request. */
343e23cd1a7Sjdolecek static int
lpioctl(struct ifnet * ifp,u_long cmd,void * data)34453524e44Schristos lpioctl(struct ifnet *ifp, u_long cmd, void *data)
345e23cd1a7Sjdolecek {
3465db50545Scegger 	struct lp_softc * sc = ifp->if_softc;
3475db50545Scegger 	device_t dev = sc->ppbus_dev.sc_dev;
348b849cd90Scegger 	device_t ppbus = device_parent(dev);
349e23cd1a7Sjdolecek 	struct ifaddr * ifa = (struct ifaddr *)data;
350e23cd1a7Sjdolecek 	struct ifreq * ifr = (struct ifreq *)data;
351e23cd1a7Sjdolecek 	u_char * ptr;
352e23cd1a7Sjdolecek 	int error, s;
353e23cd1a7Sjdolecek 
354e23cd1a7Sjdolecek 	error = 0;
355e23cd1a7Sjdolecek 	s = splnet();
356e23cd1a7Sjdolecek 
357e23cd1a7Sjdolecek 	if (sc->sc_dev_ok) {
358e23cd1a7Sjdolecek 		LP_PRINTF("%s(%s): device not properly attached!", __func__,
3591b044f41Scegger 			device_xname(dev));
360e23cd1a7Sjdolecek 		error = ENODEV;
361e23cd1a7Sjdolecek 		goto end;
362e23cd1a7Sjdolecek 	}
363e23cd1a7Sjdolecek 
364e23cd1a7Sjdolecek 	switch (cmd) {
365e23cd1a7Sjdolecek 
366e23cd1a7Sjdolecek 	case SIOCSIFDSTADDR:
367e23cd1a7Sjdolecek 		if (ifa->ifa_addr->sa_family != AF_INET)
368e23cd1a7Sjdolecek 			error = EAFNOSUPPORT;
369e23cd1a7Sjdolecek 		break;
370e23cd1a7Sjdolecek 
371de87fe67Sdyoung 	case SIOCINITIFADDR:
372e23cd1a7Sjdolecek 		if (ifa->ifa_addr->sa_family != AF_INET) {
373e23cd1a7Sjdolecek 			error = EAFNOSUPPORT;
374e23cd1a7Sjdolecek 			break;
375e23cd1a7Sjdolecek 		}
376e23cd1a7Sjdolecek 		ifp->if_flags |= IFF_UP;
377e23cd1a7Sjdolecek 	/* FALLTHROUGH */
378e23cd1a7Sjdolecek 	case SIOCSIFFLAGS:
3798e369cccSmlelstv 		if (cmd == SIOCSIFFLAGS) {
380de87fe67Sdyoung 			if ((error = ifioctl_common(ifp, cmd, data)) != 0)
381de87fe67Sdyoung 				break;
3828e369cccSmlelstv 		}
383e23cd1a7Sjdolecek 		if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_UP) {
384e23cd1a7Sjdolecek 			if ((error = ppbus_request_bus(ppbus, dev, 0, 0)))
385e23cd1a7Sjdolecek 				break;
386e23cd1a7Sjdolecek 			error = ppbus_set_mode(ppbus, PPBUS_COMPATIBLE, 0);
387e23cd1a7Sjdolecek 			if (error)
388e23cd1a7Sjdolecek 				break;
389e23cd1a7Sjdolecek 
390e23cd1a7Sjdolecek 			error = ppbus_add_handler(ppbus, lp_intr, dev);
391e23cd1a7Sjdolecek 			if (error) {
392e23cd1a7Sjdolecek 				LP_PRINTF("%s(%s): unable to register interrupt"
393e23cd1a7Sjdolecek 					" callback.\n", __func__,
3941b044f41Scegger 					device_xname(dev));
395e23cd1a7Sjdolecek 				ppbus_release_bus(ppbus, dev, 0, 0);
396e23cd1a7Sjdolecek 				break;
397e23cd1a7Sjdolecek 			}
398e23cd1a7Sjdolecek 
399e23cd1a7Sjdolecek 			/* Allocate a buffer if necessary */
400e23cd1a7Sjdolecek 			if (sc->sc_ifbuf == NULL) {
401e23cd1a7Sjdolecek 				sc->sc_ifbuf = malloc(sc->sc_if.if_mtu +
402d47bcd29Schs 					MLPIPHDRLEN, M_DEVBUF, M_WAITOK);
403e23cd1a7Sjdolecek 			}
404e23cd1a7Sjdolecek 
405e23cd1a7Sjdolecek 			ppbus_wctr(ppbus, IRQENABLE);
406e23cd1a7Sjdolecek 			ifp->if_flags |= IFF_RUNNING;
407e23cd1a7Sjdolecek 		}
408e23cd1a7Sjdolecek 		if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_RUNNING) {
409e23cd1a7Sjdolecek 			ppbus_remove_handler(ppbus, lp_intr);
410e23cd1a7Sjdolecek 			error = ppbus_release_bus(ppbus, dev, 0, 0);
411e23cd1a7Sjdolecek 			ifp->if_flags &= ~IFF_RUNNING;
412e23cd1a7Sjdolecek 		}
413e23cd1a7Sjdolecek 		/* Go quiescent */
414e23cd1a7Sjdolecek 		ppbus_wdtr(ppbus, 0);
415e23cd1a7Sjdolecek 		break;
416e23cd1a7Sjdolecek 
417e23cd1a7Sjdolecek 	case SIOCSIFMTU:
418e23cd1a7Sjdolecek 		if (sc->sc_if.if_mtu == ifr->ifr_mtu)
419e23cd1a7Sjdolecek 			break;
420e23cd1a7Sjdolecek 		ptr = sc->sc_ifbuf;
421e23cd1a7Sjdolecek 		sc->sc_ifbuf = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF,
422d47bcd29Schs 			M_WAITOK);
423e23cd1a7Sjdolecek 		if (ptr)
424e23cd1a7Sjdolecek 			free(ptr,M_DEVBUF);
4252ccede0aSdyoung 		/*FALLTHROUGH*/
426e23cd1a7Sjdolecek 	case SIOCGIFMTU:
42757f80cdbScegger 		if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
4282ccede0aSdyoung 			error = 0;
429e23cd1a7Sjdolecek 		break;
430e23cd1a7Sjdolecek 
431e23cd1a7Sjdolecek 	case SIOCADDMULTI:
432e23cd1a7Sjdolecek 	case SIOCDELMULTI:
433e23cd1a7Sjdolecek 		if (ifr == NULL) {
434e23cd1a7Sjdolecek 			error = EAFNOSUPPORT;		/* XXX */
435e23cd1a7Sjdolecek 			break;
436e23cd1a7Sjdolecek 		}
4372fc10275Sdyoung 		switch (ifreq_getaddr(cmd, ifr)->sa_family) {
438e23cd1a7Sjdolecek 		case AF_INET:
439e23cd1a7Sjdolecek 			break;
440e23cd1a7Sjdolecek 		default:
44149bf63c1Smaxv 			splx(s);
442e23cd1a7Sjdolecek 			return EAFNOSUPPORT;
443e23cd1a7Sjdolecek 		}
444e23cd1a7Sjdolecek 		break;
445e23cd1a7Sjdolecek 
446e23cd1a7Sjdolecek 	case SIOCGIFMEDIA:
447e23cd1a7Sjdolecek 		/*
448e23cd1a7Sjdolecek 		 * No ifmedia support at this stage; maybe use it
449e23cd1a7Sjdolecek 		 * in future for eg. protocol selection.
450e23cd1a7Sjdolecek 		 */
451e23cd1a7Sjdolecek 	default:
452e23cd1a7Sjdolecek 		LP_PRINTF("LP:ioctl(0x%lx)\n", cmd);
453de87fe67Sdyoung 		error = ifioctl_common(ifp, cmd, data);
454e23cd1a7Sjdolecek 	}
455e23cd1a7Sjdolecek 
456e23cd1a7Sjdolecek end:
457e23cd1a7Sjdolecek 	splx(s);
458e23cd1a7Sjdolecek 	return error;
459e23cd1a7Sjdolecek }
460e23cd1a7Sjdolecek 
46193124077Sperry static inline int
clpoutbyte(u_char byte,int spin,device_t ppbus)462b849cd90Scegger clpoutbyte(u_char byte, int spin, device_t ppbus)
463e23cd1a7Sjdolecek {
464e23cd1a7Sjdolecek 	int s = spin;
465e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, ctxmitl[byte]);
466e23cd1a7Sjdolecek 	while (ppbus_rstr(ppbus) & CLPIP_SHAKE) {
467e23cd1a7Sjdolecek 		if (--s == 0) {
468e23cd1a7Sjdolecek 			return 1;
469e23cd1a7Sjdolecek 		}
470e23cd1a7Sjdolecek 	}
471e23cd1a7Sjdolecek 	s = spin;
472e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, ctxmith[byte]);
473e23cd1a7Sjdolecek 	while (!(ppbus_rstr(ppbus) & CLPIP_SHAKE)) {
474e23cd1a7Sjdolecek 		if (--s == 0) {
475e23cd1a7Sjdolecek 			return 1;
476e23cd1a7Sjdolecek 		}
477e23cd1a7Sjdolecek 	}
478e23cd1a7Sjdolecek 	return 0;
479e23cd1a7Sjdolecek }
480e23cd1a7Sjdolecek 
48193124077Sperry static inline int
clpinbyte(int spin,device_t ppbus)482b849cd90Scegger clpinbyte(int spin, device_t ppbus)
483e23cd1a7Sjdolecek {
484e23cd1a7Sjdolecek 	u_char c, cl;
485e23cd1a7Sjdolecek 	int s = spin;
486e23cd1a7Sjdolecek 
487e23cd1a7Sjdolecek 	while (ppbus_rstr(ppbus) & CLPIP_SHAKE) {
488e23cd1a7Sjdolecek 		if (!--s) {
489e23cd1a7Sjdolecek 			return -1;
490e23cd1a7Sjdolecek 		}
491e23cd1a7Sjdolecek 	}
492e23cd1a7Sjdolecek 	cl = ppbus_rstr(ppbus);
493e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, 0x10);
494e23cd1a7Sjdolecek 
495e23cd1a7Sjdolecek 	s = spin;
496e23cd1a7Sjdolecek 	while (!(ppbus_rstr(ppbus) & CLPIP_SHAKE)) {
497e23cd1a7Sjdolecek 		if (!--s) {
498e23cd1a7Sjdolecek 			return -1;
499e23cd1a7Sjdolecek 		}
500e23cd1a7Sjdolecek 	}
501e23cd1a7Sjdolecek 	c = ppbus_rstr(ppbus);
502e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, 0x00);
503e23cd1a7Sjdolecek 
504e23cd1a7Sjdolecek 	return (ctrecvl[cl] | ctrecvh[c]);
505e23cd1a7Sjdolecek }
506e23cd1a7Sjdolecek 
507e23cd1a7Sjdolecek static void
lptap(struct ifnet * ifp,struct mbuf * m,u_int direction)5083cd62456Smsaitoh lptap(struct ifnet *ifp, struct mbuf *m, u_int direction)
509e23cd1a7Sjdolecek {
510e23cd1a7Sjdolecek 	/*
511e23cd1a7Sjdolecek 	 * Send a packet through bpf. We need to prepend the address family
512e23cd1a7Sjdolecek 	 * as a four byte field. Cons up a dummy header to pacify bpf. This
513e23cd1a7Sjdolecek 	 * is safe because bpf will only read from the mbuf (i.e., it won't
514e23cd1a7Sjdolecek 	 * try to free it or keep a pointer to it).
515e23cd1a7Sjdolecek 	 */
516e23cd1a7Sjdolecek 	u_int32_t af = AF_INET;
517e23cd1a7Sjdolecek 	struct mbuf m0;
518e23cd1a7Sjdolecek 
519c33f3064Smsaitoh 	m0.m_type = MT_DATA;
520e23cd1a7Sjdolecek 	m0.m_next = m;
521c33f3064Smsaitoh 	m0.m_nextpkt = NULL;
522c33f3064Smsaitoh 	m0.m_owner = NULL;
523e23cd1a7Sjdolecek 	m0.m_len = sizeof(u_int32_t);
524e23cd1a7Sjdolecek 	m0.m_data = (char *)&af;
525c33f3064Smsaitoh 	m0.m_flags = 0;
5263cd62456Smsaitoh 	bpf_mtap(ifp, &m0, direction);
527e23cd1a7Sjdolecek }
528e23cd1a7Sjdolecek 
529e23cd1a7Sjdolecek /* Soft interrupt handler called by hardware interrupt handler */
530e23cd1a7Sjdolecek static void
lp_intr(void * arg)531e23cd1a7Sjdolecek lp_intr(void *arg)
532e23cd1a7Sjdolecek {
533b849cd90Scegger 	device_t dev = (device_t)arg;
534b849cd90Scegger         device_t ppbus = device_parent(dev);
535b849cd90Scegger 	struct lp_softc * sc = device_private(dev);
536e23cd1a7Sjdolecek 	struct ifnet * ifp = &sc->sc_if;
537e23cd1a7Sjdolecek 	struct mbuf *top;
538e23cd1a7Sjdolecek 	int len, s, j;
539e23cd1a7Sjdolecek 	u_char *bp;
540e23cd1a7Sjdolecek 	u_char c, cl;
541e23cd1a7Sjdolecek 
542e23cd1a7Sjdolecek 	s = splnet();
543e23cd1a7Sjdolecek 
544e23cd1a7Sjdolecek 	/* Do nothing if device not properly attached */
545e23cd1a7Sjdolecek 	if (sc->sc_dev_ok) {
546e23cd1a7Sjdolecek 		LP_PRINTF("%s(%s): device not properly attached!", __func__,
5471b044f41Scegger 			device_xname(dev));
548e23cd1a7Sjdolecek 		goto done;
549e23cd1a7Sjdolecek 	}
550e23cd1a7Sjdolecek 
551e23cd1a7Sjdolecek 	/* Do nothing if interface is not up */
552e23cd1a7Sjdolecek 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
553e23cd1a7Sjdolecek 		goto done;
554e23cd1a7Sjdolecek 
555e23cd1a7Sjdolecek 	/* If other side is no longer transmitting, do nothing */
556e23cd1a7Sjdolecek 	if (!(ppbus_rstr(ppbus) & LPIP_SHAKE))
557e23cd1a7Sjdolecek 		goto done;
558e23cd1a7Sjdolecek 
559e23cd1a7Sjdolecek 	/* Disable interrupts until we finish */
560e23cd1a7Sjdolecek 	ppbus_wctr(ppbus, ~IRQENABLE);
561e23cd1a7Sjdolecek 
562e23cd1a7Sjdolecek 	top = NULL;
563e23cd1a7Sjdolecek 	bp = sc->sc_ifbuf;
564e23cd1a7Sjdolecek 	/* Linux/crynwyr protocol receiving */
565e23cd1a7Sjdolecek 	if (ifp->if_flags & IFF_LINK0) {
566e23cd1a7Sjdolecek 		/* Ack. the request */
567e23cd1a7Sjdolecek 		ppbus_wdtr(ppbus, 0x01);
568e23cd1a7Sjdolecek 
569e23cd1a7Sjdolecek 		/* Get the packet length */
570e23cd1a7Sjdolecek 		j = clpinbyte(LPMAXSPIN2, ppbus);
571e23cd1a7Sjdolecek 		if (j == -1)
572e23cd1a7Sjdolecek 			goto err;
573e23cd1a7Sjdolecek 		len = j;
574e23cd1a7Sjdolecek 		j = clpinbyte(LPMAXSPIN2, ppbus);
575e23cd1a7Sjdolecek 		if (j == -1)
576e23cd1a7Sjdolecek 			goto err;
577e23cd1a7Sjdolecek 		len = len + (j << 8);
578e23cd1a7Sjdolecek 		if (len > ifp->if_mtu + MLPIPHDRLEN)
579e23cd1a7Sjdolecek 			goto err;
580e23cd1a7Sjdolecek 
581e23cd1a7Sjdolecek 		while (len--) {
582e23cd1a7Sjdolecek 			j = clpinbyte(LPMAXSPIN2, ppbus);
583e23cd1a7Sjdolecek 			if (j == -1) {
584e23cd1a7Sjdolecek 				goto err;
585e23cd1a7Sjdolecek 			}
586e23cd1a7Sjdolecek 			*bp++ = j;
587e23cd1a7Sjdolecek 		}
588e23cd1a7Sjdolecek 		/* Get and ignore checksum */
589e23cd1a7Sjdolecek 		j = clpinbyte(LPMAXSPIN2, ppbus);
590e23cd1a7Sjdolecek 		if (j == -1) {
591e23cd1a7Sjdolecek 			goto err;
592e23cd1a7Sjdolecek 		}
593e23cd1a7Sjdolecek 
594e23cd1a7Sjdolecek 		/* Return to idle state */
595e23cd1a7Sjdolecek 		ppbus_wdtr(ppbus, 0);
596e23cd1a7Sjdolecek 		len = bp - sc->sc_ifbuf;
597e23cd1a7Sjdolecek 		if (len <= CLPIPHDRLEN)
598e23cd1a7Sjdolecek 			goto err;
599e23cd1a7Sjdolecek 		len -= CLPIPHDRLEN;
600ec3805d3Smaxv 		top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, ifp);
601e23cd1a7Sjdolecek 	}
602e23cd1a7Sjdolecek 	/* FreeBSD protocol receiving */
603e23cd1a7Sjdolecek 	else {
604e23cd1a7Sjdolecek 		len = ifp->if_mtu + LPIPHDRLEN;
605e23cd1a7Sjdolecek 		while (len--) {
606e23cd1a7Sjdolecek 			cl = ppbus_rstr(ppbus);
607e23cd1a7Sjdolecek 			ppbus_wdtr(ppbus, 0x08);
608e23cd1a7Sjdolecek 
609e23cd1a7Sjdolecek 			j = LPMAXSPIN2;
610e23cd1a7Sjdolecek 			while ((ppbus_rstr(ppbus) & LPIP_SHAKE)) {
6113d3638f6Smsaitoh 				if (!--j)
6123d3638f6Smsaitoh 					goto err;
613e23cd1a7Sjdolecek 			}
614e23cd1a7Sjdolecek 
615e23cd1a7Sjdolecek 			c = ppbus_rstr(ppbus);
616e23cd1a7Sjdolecek 			ppbus_wdtr(ppbus, 0);
617e23cd1a7Sjdolecek 
618e23cd1a7Sjdolecek 			*bp++= trecvh[cl] | trecvl[c];
619e23cd1a7Sjdolecek 
620e23cd1a7Sjdolecek 			j = LPMAXSPIN2;
621e23cd1a7Sjdolecek 			while (!((cl = ppbus_rstr(ppbus)) & LPIP_SHAKE)) {
622e23cd1a7Sjdolecek 				if (cl != c &&
623e23cd1a7Sjdolecek 					(((cl = ppbus_rstr(ppbus)) ^ 0xb8) &
624e23cd1a7Sjdolecek 					0xf8) == (c & 0xf8))
625e23cd1a7Sjdolecek 					goto end;
6263d3638f6Smsaitoh 				if (!--j)
6273d3638f6Smsaitoh 					goto err;
628e23cd1a7Sjdolecek 			}
629e23cd1a7Sjdolecek 		}
630e23cd1a7Sjdolecek 
631e23cd1a7Sjdolecek end:
632e23cd1a7Sjdolecek 		len = bp - sc->sc_ifbuf;
633e23cd1a7Sjdolecek 		if (len <= LPIPHDRLEN)
634e23cd1a7Sjdolecek 			goto err;
635e23cd1a7Sjdolecek 		len -= LPIPHDRLEN;
636ec3805d3Smaxv 		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, ifp);
637e23cd1a7Sjdolecek 	}
638e23cd1a7Sjdolecek 
63960d350cfSrmind 	if (top == NULL) {
64031b98d95Sjdolecek 		if_statinc(ifp, if_iqdrops);
641e23cd1a7Sjdolecek 		goto err;
642e23cd1a7Sjdolecek 	}
64360d350cfSrmind 	if (ifp->if_bpf) {
6443cd62456Smsaitoh 		lptap(ifp, top, BPF_D_IN);
64560d350cfSrmind 	}
64660d350cfSrmind 	if (__predict_false(!pktq_enqueue(ip_pktq, top, 0))) {
64731b98d95Sjdolecek 		if_statinc(ifp, if_iqdrops);
64860d350cfSrmind 		m_freem(top);
64960d350cfSrmind 		goto err;
65060d350cfSrmind 	}
65131b98d95Sjdolecek 	if_statinc(ifp, if_ipackets);
65231b98d95Sjdolecek 	if_statadd(ifp, if_ibytes, len);
653e23cd1a7Sjdolecek 	sc->sc_iferrs = 0;
654e23cd1a7Sjdolecek 
655e23cd1a7Sjdolecek 	goto done;
656e23cd1a7Sjdolecek 
657e23cd1a7Sjdolecek err:
658e23cd1a7Sjdolecek 	/* Return to idle state */
659e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, 0);
66011704b98Sskrll 	if_statinc(ifp, if_ierrors);
661e23cd1a7Sjdolecek 	sc->sc_iferrs++;
662e23cd1a7Sjdolecek 	LP_PRINTF("R");
663e23cd1a7Sjdolecek 	/* Disable interface if there are too many errors */
664e23cd1a7Sjdolecek 	if (sc->sc_iferrs > LPMAXERRS) {
6651b044f41Scegger 		aprint_error_dev(dev, "Too many consecutive errors, going off-line.\n");
666e23cd1a7Sjdolecek 		ppbus_wctr(ppbus, ~IRQENABLE);
667e23cd1a7Sjdolecek 		if_down(ifp);
668e23cd1a7Sjdolecek 		sc->sc_iferrs = 0;
669e23cd1a7Sjdolecek 	}
670e23cd1a7Sjdolecek 
671e23cd1a7Sjdolecek done:
672e23cd1a7Sjdolecek 	/* Re-enable interrupts */
673e23cd1a7Sjdolecek 	ppbus_wctr(ppbus, IRQENABLE);
674e23cd1a7Sjdolecek 	/* If interface is not active, send some packets */
675e23cd1a7Sjdolecek 	if ((ifp->if_flags & IFF_OACTIVE) == 0)
676e23cd1a7Sjdolecek 		lpstart(ifp);
677e23cd1a7Sjdolecek 	splx(s);
678e23cd1a7Sjdolecek 	return;
679e23cd1a7Sjdolecek }
680e23cd1a7Sjdolecek 
68193124077Sperry static inline int
lpoutbyte(u_char byte,int spin,device_t ppbus)682b849cd90Scegger lpoutbyte(u_char byte, int spin, device_t ppbus)
683e23cd1a7Sjdolecek {
684e23cd1a7Sjdolecek 	int s = spin;
685e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, txmith[byte]);
686e23cd1a7Sjdolecek 	while (!(ppbus_rstr(ppbus) & LPIP_SHAKE)) {
687e23cd1a7Sjdolecek 		if (--s == 0)
688e23cd1a7Sjdolecek 			return 1;
689e23cd1a7Sjdolecek 	}
690e23cd1a7Sjdolecek 	s = spin;
691e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, txmitl[byte]);
692e23cd1a7Sjdolecek 	while (ppbus_rstr(ppbus) & LPIP_SHAKE) {
693e23cd1a7Sjdolecek 		if (--s == 0)
694e23cd1a7Sjdolecek 			return 1;
695e23cd1a7Sjdolecek 	}
696e23cd1a7Sjdolecek 	return 0;
697e23cd1a7Sjdolecek }
698e23cd1a7Sjdolecek 
699e23cd1a7Sjdolecek /* Queue a packet for delivery */
700e23cd1a7Sjdolecek static int
lpoutput(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,const struct rtentry * rt)701bac28a1dSdrochner lpoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
70252e1f28cSchristos     const struct rtentry *rt)
703e23cd1a7Sjdolecek {
7045db50545Scegger 	struct lp_softc * sc = ifp->if_softc;
7055db50545Scegger 	device_t dev = sc->ppbus_dev.sc_dev;
706b849cd90Scegger 	device_t ppbus = device_parent(dev);
707e23cd1a7Sjdolecek 	int err;
708e23cd1a7Sjdolecek 	int s;
709e23cd1a7Sjdolecek 
710e23cd1a7Sjdolecek 	s = splnet();
711e23cd1a7Sjdolecek 
712e23cd1a7Sjdolecek 	if (sc->sc_dev_ok) {
713e23cd1a7Sjdolecek 		LP_PRINTF("%s(%s): device not properly attached!", __func__,
7141b044f41Scegger 			device_xname(dev));
715e23cd1a7Sjdolecek 		err = ENODEV;
716e23cd1a7Sjdolecek 		goto endoutput;
717e23cd1a7Sjdolecek 	}
718e23cd1a7Sjdolecek 
719e23cd1a7Sjdolecek 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
720e23cd1a7Sjdolecek 		err = ENETDOWN;
721e23cd1a7Sjdolecek 		goto endoutput;
722e23cd1a7Sjdolecek 	}
723e23cd1a7Sjdolecek 
724e23cd1a7Sjdolecek 	/* Only support INET */
725e23cd1a7Sjdolecek 	if (dst->sa_family != AF_INET) {
726e23cd1a7Sjdolecek 		LP_PRINTF("%s: af%d not supported\n", ifp->if_xname,
727e23cd1a7Sjdolecek 		    dst->sa_family);
72831b98d95Sjdolecek 		if_statinc(ifp, if_noproto);
729e23cd1a7Sjdolecek 		err = EAFNOSUPPORT;
730e23cd1a7Sjdolecek 		goto endoutput;
731e23cd1a7Sjdolecek 	}
732e23cd1a7Sjdolecek 
733b76ec0b0Sknakahara 	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
73452e1f28cSchristos 	IFQ_ENQUEUE(&ifp->if_snd, m, err);
735e23cd1a7Sjdolecek 	if (err == 0) {
736e23cd1a7Sjdolecek 		if ((ifp->if_flags & IFF_OACTIVE) == 0)
737e23cd1a7Sjdolecek 			lpstart(ifp);
7383d3638f6Smsaitoh 	} else {
73911704b98Sskrll 		if_statinc(ifp, if_oerrors);
740e23cd1a7Sjdolecek 		sc->sc_iferrs++;
741e23cd1a7Sjdolecek 		LP_PRINTF("Q");
742e23cd1a7Sjdolecek 
743e23cd1a7Sjdolecek 		/* Disable interface if there are too many errors */
744e23cd1a7Sjdolecek 		if (sc->sc_iferrs > LPMAXERRS) {
7451b044f41Scegger 			aprint_error_dev(dev, "Too many errors, going off-line.\n");
746e23cd1a7Sjdolecek 			ppbus_wctr(ppbus, ~IRQENABLE);
747e23cd1a7Sjdolecek 			if_down(ifp);
748e23cd1a7Sjdolecek 			sc->sc_iferrs = 0;
749e23cd1a7Sjdolecek 		}
750e23cd1a7Sjdolecek 	}
751e23cd1a7Sjdolecek 
752e23cd1a7Sjdolecek endoutput:
753e23cd1a7Sjdolecek 	if ((err != 0) && (err != ENOBUFS))
754e23cd1a7Sjdolecek 		m_freem(m);
755e23cd1a7Sjdolecek 	splx(s);
756e23cd1a7Sjdolecek 	return err;
757e23cd1a7Sjdolecek }
758e23cd1a7Sjdolecek 
759e23cd1a7Sjdolecek /* Send routine: send packets over PLIP cable. Call at splnet(). */
760e23cd1a7Sjdolecek void
lpstart(struct ifnet * ifp)761e23cd1a7Sjdolecek lpstart(struct ifnet * ifp)
762e23cd1a7Sjdolecek {
763e23cd1a7Sjdolecek 	struct lp_softc * lp = ifp->if_softc;
7645db50545Scegger 	device_t dev = lp->ppbus_dev.sc_dev;
765b849cd90Scegger 	device_t ppbus = device_parent(dev);
766e23cd1a7Sjdolecek 	struct mbuf * mm;
767e23cd1a7Sjdolecek 	struct mbuf * m;
768e23cd1a7Sjdolecek 	u_char * cp;
769e23cd1a7Sjdolecek 	int err, i, len, spin, count;
770e23cd1a7Sjdolecek 	u_char str, chksum;
771e23cd1a7Sjdolecek 
772e23cd1a7Sjdolecek 	if (lp->sc_dev_ok) {
773e23cd1a7Sjdolecek 		LP_PRINTF("%s(%s): device not properly attached!", __func__,
7741b044f41Scegger 			device_xname(dev));
775e23cd1a7Sjdolecek 		return;
776e23cd1a7Sjdolecek 	}
777e23cd1a7Sjdolecek 
778e23cd1a7Sjdolecek 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
779e23cd1a7Sjdolecek 		return;
780e23cd1a7Sjdolecek 	}
781e23cd1a7Sjdolecek 
782e23cd1a7Sjdolecek 	ifp->if_flags |= IFF_OACTIVE;
783e23cd1a7Sjdolecek 
784e23cd1a7Sjdolecek 	/* Go quiescent */
785e23cd1a7Sjdolecek 	ppbus_wdtr(ppbus, 0);
786e23cd1a7Sjdolecek 
787e23cd1a7Sjdolecek 	/* Output loop */
788e23cd1a7Sjdolecek 	for (;;) {
789e23cd1a7Sjdolecek 		/* Check if there are packets to send */
790e23cd1a7Sjdolecek 		if (IFQ_IS_EMPTY(&ifp->if_snd)) {
791e23cd1a7Sjdolecek 			goto final;
792e23cd1a7Sjdolecek 		}
793e23cd1a7Sjdolecek 		/* Try to send a packet, dequeue it later if successful */
794e23cd1a7Sjdolecek 		IFQ_POLL(&ifp->if_snd, m);
795e23cd1a7Sjdolecek 		if (m == NULL)
796e23cd1a7Sjdolecek 			goto final;
797e23cd1a7Sjdolecek 
798e23cd1a7Sjdolecek 		str = ppbus_rstr(ppbus);
799e23cd1a7Sjdolecek 		/* Wait until other side is not transmitting */
800e23cd1a7Sjdolecek 		if ((str & LPIP_SHAKE) ||
801e23cd1a7Sjdolecek 			((ifp->if_flags & IFF_LINK0) && !(str & CLPIP_SHAKE))) {
802e23cd1a7Sjdolecek 			LP_PRINTF("&");
803e23cd1a7Sjdolecek 			if (++lp->sc_xmit_rtry > LPMAXRTRY) {
8041b044f41Scegger 				aprint_error_dev(dev, "Too many retries while channel "
8051b044f41Scegger 					"busy, going off-line.\n");
806e23cd1a7Sjdolecek 				ppbus_wctr(ppbus, ~IRQENABLE);
807e23cd1a7Sjdolecek 				if_down(ifp);
808e23cd1a7Sjdolecek 				lp->sc_xmit_rtry = 0;
809e23cd1a7Sjdolecek 			}
810e23cd1a7Sjdolecek 			goto final;
811e23cd1a7Sjdolecek 		}
812e23cd1a7Sjdolecek 		lp->sc_xmit_rtry = 0;
813e23cd1a7Sjdolecek 
814e23cd1a7Sjdolecek 		/* Disable interrupt generation */
815e23cd1a7Sjdolecek 		ppbus_wctr(ppbus, ~IRQENABLE);
816e23cd1a7Sjdolecek 
817e23cd1a7Sjdolecek 		err = 1;
818e23cd1a7Sjdolecek 
819e23cd1a7Sjdolecek 		/* Output packet for Linux/crynwyr compatible protocol */
820e23cd1a7Sjdolecek 		if (ifp->if_flags & IFF_LINK0) {
821e23cd1a7Sjdolecek 			/* Calculate packet length */
822e23cd1a7Sjdolecek 			count = 14;		/* Ethernet header len */
823e23cd1a7Sjdolecek 			for (mm = m; mm; mm = mm->m_next) {
824e23cd1a7Sjdolecek 				count += mm->m_len;
825e23cd1a7Sjdolecek 			}
826e23cd1a7Sjdolecek 
827e23cd1a7Sjdolecek 			/* Alert other end to pending packet */
828e23cd1a7Sjdolecek 			spin = LPMAXSPIN1;
829e23cd1a7Sjdolecek 			ppbus_wdtr(ppbus, 0x08);
830e23cd1a7Sjdolecek 			while ((ppbus_rstr(ppbus) & 0x08) == 0) {
831e23cd1a7Sjdolecek 				if (--spin == 0) {
832e23cd1a7Sjdolecek 					goto nend;
833e23cd1a7Sjdolecek 				}
834e23cd1a7Sjdolecek 			}
835e23cd1a7Sjdolecek 
836e23cd1a7Sjdolecek 			if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
837e23cd1a7Sjdolecek 				goto nend;
838e23cd1a7Sjdolecek 			if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
839e23cd1a7Sjdolecek 				goto nend;
840e23cd1a7Sjdolecek 
841e23cd1a7Sjdolecek 			/* Send dummy ethernet header */
842e23cd1a7Sjdolecek 			chksum = 0;
843e23cd1a7Sjdolecek 			for (i = 0; i < 12; i++) {
844e23cd1a7Sjdolecek 				if (clpoutbyte(i, LPMAXSPIN1, ppbus))
845e23cd1a7Sjdolecek 					goto nend;
846e23cd1a7Sjdolecek 				chksum += i;
847e23cd1a7Sjdolecek 			}
848e23cd1a7Sjdolecek 
849e23cd1a7Sjdolecek 			if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
850e23cd1a7Sjdolecek 				goto nend;
851e23cd1a7Sjdolecek 			if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
852e23cd1a7Sjdolecek 				goto nend;
853e23cd1a7Sjdolecek 			chksum += 0x08 + 0x00;		/* Add into checksum */
854e23cd1a7Sjdolecek 
855e23cd1a7Sjdolecek 			mm = m;
856e23cd1a7Sjdolecek 			do {
857e23cd1a7Sjdolecek 				cp = mtod(mm, u_char *);
858e23cd1a7Sjdolecek 				len = mm->m_len;
859e23cd1a7Sjdolecek 				while (len--) {
860e23cd1a7Sjdolecek 					if (clpoutbyte(*cp, LPMAXSPIN2, ppbus))
861e23cd1a7Sjdolecek 						goto nend;
862e23cd1a7Sjdolecek 					chksum += *cp++;
863e23cd1a7Sjdolecek 				}
864e23cd1a7Sjdolecek 			} while ((mm = mm->m_next));
865e23cd1a7Sjdolecek 
866e23cd1a7Sjdolecek 			/* Send checksum */
867e23cd1a7Sjdolecek 			if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
868e23cd1a7Sjdolecek 				goto nend;
869e23cd1a7Sjdolecek 
870e23cd1a7Sjdolecek 			/* No errors */
871e23cd1a7Sjdolecek 			err = 0;
872e23cd1a7Sjdolecek 			/* Go quiescent */
873e23cd1a7Sjdolecek 			ppbus_wdtr(ppbus, 0);
874e23cd1a7Sjdolecek 		}
875e23cd1a7Sjdolecek 		/* Output packet for FreeBSD compatible protocol */
876e23cd1a7Sjdolecek 		else {
877e23cd1a7Sjdolecek 			/* We need a sensible value if we abort */
878e23cd1a7Sjdolecek 			cp = NULL;
879e23cd1a7Sjdolecek 
880e23cd1a7Sjdolecek 			if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
881e23cd1a7Sjdolecek 				goto end;
882e23cd1a7Sjdolecek 			if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
883e23cd1a7Sjdolecek 				goto end;
884e23cd1a7Sjdolecek 
885e23cd1a7Sjdolecek 			mm = m;
886e23cd1a7Sjdolecek 			do {
887e23cd1a7Sjdolecek 				cp = mtod(mm,u_char *);
888e23cd1a7Sjdolecek 				len = mm->m_len;
889e23cd1a7Sjdolecek 				while (len--)
890e23cd1a7Sjdolecek 					if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
891e23cd1a7Sjdolecek 						goto end;
892e23cd1a7Sjdolecek 			} while ((mm = mm->m_next));
893e23cd1a7Sjdolecek 
894e23cd1a7Sjdolecek 			/* no errors were encountered */
895e23cd1a7Sjdolecek 			err = 0;
896e23cd1a7Sjdolecek 
897e23cd1a7Sjdolecek end:
898e23cd1a7Sjdolecek 			if (cp)
899e23cd1a7Sjdolecek 				ppbus_wdtr(ppbus, txmitl[*(--cp)] ^ 0x17);
900e23cd1a7Sjdolecek 			else
901e23cd1a7Sjdolecek 				ppbus_wdtr(ppbus, txmitl['\0'] ^ 0x17);
902e23cd1a7Sjdolecek 		}
903e23cd1a7Sjdolecek 
904ebe00413Sjdolecek nend:
905e23cd1a7Sjdolecek 		/* Re-enable interrupt generation */
906e23cd1a7Sjdolecek 		ppbus_wctr(ppbus, IRQENABLE);
907e23cd1a7Sjdolecek 
908e23cd1a7Sjdolecek 		if (err) {
909e23cd1a7Sjdolecek 			/* Go quiescent */
910e23cd1a7Sjdolecek 			ppbus_wdtr(ppbus, 0);
911e23cd1a7Sjdolecek 
91211704b98Sskrll 			if_statinc(ifp, if_oerrors);
913e23cd1a7Sjdolecek 			lp->sc_iferrs++;
914e23cd1a7Sjdolecek 			LP_PRINTF("X");
915e23cd1a7Sjdolecek 
916e23cd1a7Sjdolecek 			/* Disable interface if there are too many errors */
917e23cd1a7Sjdolecek 			if (lp->sc_iferrs > LPMAXERRS) {
9181b044f41Scegger 				aprint_error_dev(dev, "Too many errors, going off-line.\n");
919e23cd1a7Sjdolecek 				ppbus_wctr(ppbus, ~IRQENABLE);
920e23cd1a7Sjdolecek 				if_down(ifp);
921e23cd1a7Sjdolecek 				lp->sc_iferrs = 0;
922e23cd1a7Sjdolecek 				goto final;
923e23cd1a7Sjdolecek 			}
9243d3638f6Smsaitoh 		} else {
925e23cd1a7Sjdolecek 			/* Dequeue packet on success */
926e23cd1a7Sjdolecek 			IFQ_DEQUEUE(&ifp->if_snd, m);
927e23cd1a7Sjdolecek 			if(ifp->if_bpf)
9283cd62456Smsaitoh 				lptap(ifp, m, BPF_D_OUT);
92911704b98Sskrll 			if_statinc(ifp, if_opackets);
93011704b98Sskrll 			if_statadd(ifp, if_obytes, m->m_pkthdr.len);
931e23cd1a7Sjdolecek 			m_freem(m);
932e23cd1a7Sjdolecek 		}
933e23cd1a7Sjdolecek 	}
934e23cd1a7Sjdolecek 
935e23cd1a7Sjdolecek final:
936e23cd1a7Sjdolecek 	ifp->if_flags &= ~IFF_OACTIVE;
937e23cd1a7Sjdolecek 	return;
938e23cd1a7Sjdolecek }
939