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 *)⁡
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