138061Smsmith/*-
238061Smsmith * Copyright (c) 1997 Poul-Henning Kamp
338061Smsmith * All rights reserved.
438061Smsmith *
538061Smsmith * Redistribution and use in source and binary forms, with or without
638061Smsmith * modification, are permitted provided that the following conditions
738061Smsmith * are met:
838061Smsmith * 1. Redistributions of source code must retain the above copyright
938061Smsmith *    notice, this list of conditions and the following disclaimer.
1038061Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138061Smsmith *    notice, this list of conditions and the following disclaimer in the
1238061Smsmith *    documentation and/or other materials provided with the distribution.
1338061Smsmith *
1438061Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538061Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638061Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738061Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838061Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938061Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038061Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138061Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238061Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338061Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438061Smsmith * SUCH DAMAGE.
2538061Smsmith *
2638061Smsmith *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
2738061Smsmith */
2838061Smsmith
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD$");
31119418Sobrien
3238061Smsmith/*
3338061Smsmith * Parallel port TCP/IP interfaces added.  I looked at the driver from
3438061Smsmith * MACH but this is a complete rewrite, and btw. incompatible, and it
3538061Smsmith * should perform better too.  I have never run the MACH driver though.
3638061Smsmith *
3738061Smsmith * This driver sends two bytes (0x08, 0x00) in front of each packet,
3838061Smsmith * to allow us to distinguish another format later.
3938061Smsmith *
40108470Sschweikh * Now added a Linux/Crynwr compatibility mode which is enabled using
4138061Smsmith * IF_LINK0 - Tim Wilkinson.
4238061Smsmith *
4338061Smsmith * TODO:
4438061Smsmith *    Make HDLC/PPP mode, use IF_LLC1 to enable.
4538061Smsmith *
4638061Smsmith * Connect the two computers using a Laplink parallel cable to use this
4738061Smsmith * feature:
4838061Smsmith *
4938061Smsmith *      +----------------------------------------+
5038061Smsmith * 	|A-name	A-End	B-End	Descr.	Port/Bit |
5138061Smsmith *      +----------------------------------------+
5238061Smsmith *	|DATA0	2	15	Data	0/0x01   |
5338061Smsmith *	|-ERROR	15	2	   	1/0x08   |
5438061Smsmith *      +----------------------------------------+
5538061Smsmith *	|DATA1	3	13	Data	0/0x02	 |
5638061Smsmith *	|+SLCT	13	3	   	1/0x10   |
5738061Smsmith *      +----------------------------------------+
5838061Smsmith *	|DATA2	4	12	Data	0/0x04   |
5938061Smsmith *	|+PE	12	4	   	1/0x20   |
6038061Smsmith *      +----------------------------------------+
6138061Smsmith *	|DATA3	5	10	Strobe	0/0x08   |
6238061Smsmith *	|-ACK	10	5	   	1/0x40   |
6338061Smsmith *      +----------------------------------------+
6438061Smsmith *	|DATA4	6	11	Data	0/0x10   |
6538061Smsmith *	|BUSY	11	6	   	1/~0x80  |
6638061Smsmith *      +----------------------------------------+
6738061Smsmith *	|GND	18-25	18-25	GND	-        |
6838061Smsmith *      +----------------------------------------+
6938061Smsmith *
7038061Smsmith * Expect transfer-rates up to 75 kbyte/sec.
7138061Smsmith *
7238061Smsmith * If GCC could correctly grok
7338061Smsmith *	register int port asm("edx")
7438061Smsmith * the code would be cleaner
7538061Smsmith *
7638061Smsmith * Poul-Henning Kamp <phk@freebsd.org>
7738061Smsmith */
7838061Smsmith
7938061Smsmith/*
8038061Smsmith * Update for ppbus, PLIP support only - Nicolas Souchu
81184896Sjhb */
8238061Smsmith
8355939Snsouch#include "opt_plip.h"
8455939Snsouch
8538061Smsmith#include <sys/param.h>
8638061Smsmith#include <sys/systm.h>
8755939Snsouch#include <sys/module.h>
8855939Snsouch#include <sys/bus.h>
8938061Smsmith#include <sys/mbuf.h>
9038061Smsmith#include <sys/socket.h>
9138061Smsmith#include <sys/sockio.h>
9238061Smsmith#include <sys/kernel.h>
9338061Smsmith#include <sys/malloc.h>
9438061Smsmith
9555939Snsouch#include <machine/bus.h>
9655939Snsouch#include <machine/resource.h>
9755939Snsouch#include <sys/rman.h>
9855939Snsouch
9938061Smsmith#include <net/if.h>
10038061Smsmith#include <net/if_types.h>
10138061Smsmith#include <net/netisr.h>
102191148Skmacy#include <net/route.h>
10338061Smsmith
10438061Smsmith#include <netinet/in.h>
10538061Smsmith#include <netinet/in_var.h>
10638061Smsmith
10738061Smsmith#include <net/bpf.h>
10838061Smsmith
10938061Smsmith#include <dev/ppbus/ppbconf.h>
11055939Snsouch#include "ppbus_if.h"
11155939Snsouch#include <dev/ppbus/ppbio.h>
11238061Smsmith
11338061Smsmith#ifndef LPMTU			/* MTU for the lp# interfaces */
114184896Sjhb#define	LPMTU		1500
11538061Smsmith#endif
11638061Smsmith
11738061Smsmith#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
11838061Smsmith#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
11938061Smsmith#endif
12038061Smsmith
12138061Smsmith#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
12238061Smsmith#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
12338061Smsmith#endif
12438061Smsmith
12538061Smsmith#ifndef LPMAXERRS		/* Max errors before !RUNNING */
12638061Smsmith#define	LPMAXERRS	100
12738061Smsmith#endif
12838061Smsmith
129185003Sjhb#define	CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
13038061Smsmith#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
131185003Sjhb#define	MLPIPHDRLEN	CLPIPHDRLEN
13238061Smsmith
133185003Sjhb#define	LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
13438061Smsmith#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
13538061Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
136185003Sjhb#define	MLPIPHDRLEN	LPIPHDRLEN
13738061Smsmith#endif
13838061Smsmith
13938061Smsmith#define	LPIPTBLSIZE	256	/* Size of octet translation table */
14038061Smsmith
141185003Sjhb#define	lprintf		if (lptflag) printf
14243433Snsouch
14343433Snsouch#ifdef PLIP_DEBUG
14438061Smsmithstatic int volatile lptflag = 1;
14543433Snsouch#else
14643433Snsouchstatic int volatile lptflag = 0;
14738061Smsmith#endif
14838061Smsmith
14955939Snsouchstruct lp_data {
150147256Sbrooks	struct  ifnet	*sc_ifp;
151184130Sjhb	device_t	sc_dev;
15238061Smsmith	u_char		*sc_ifbuf;
15338061Smsmith	int		sc_iferrs;
15455939Snsouch
15555939Snsouch	struct resource *res_irq;
156187576Sjhb	void		*sc_intr_cookie;
15738061Smsmith};
15838061Smsmith
159187576Sjhbstatic struct mtx lp_tables_lock;
160187576SjhbMTX_SYSINIT(lp_tables, &lp_tables_lock, "plip tables", MTX_DEF);
161187576Sjhb
16238061Smsmith/* Tables for the lp# interface */
16338061Smsmithstatic u_char *txmith;
164185003Sjhb#define	txmitl (txmith + (1 * LPIPTBLSIZE))
165185003Sjhb#define	trecvh (txmith + (2 * LPIPTBLSIZE))
166185003Sjhb#define	trecvl (txmith + (3 * LPIPTBLSIZE))
16738061Smsmith
16838061Smsmithstatic u_char *ctxmith;
169185003Sjhb#define	ctxmitl (ctxmith + (1 * LPIPTBLSIZE))
170185003Sjhb#define	ctrecvh (ctxmith + (2 * LPIPTBLSIZE))
171185003Sjhb#define	ctrecvl (ctxmith + (3 * LPIPTBLSIZE))
17238061Smsmith
17338061Smsmith/* Functions for the lp# interface */
17438061Smsmithstatic int lpinittables(void);
17538061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t);
176249925Sglebiusstatic int lpoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
177191148Skmacy       struct route *);
178187576Sjhbstatic void lpstop(struct lp_data *);
17955939Snsouchstatic void lp_intr(void *);
180187576Sjhbstatic int lp_module_handler(module_t, int, void *);
18138061Smsmith
182185003Sjhb#define	DEVTOSOFTC(dev) \
18355939Snsouch	((struct lp_data *)device_get_softc(dev))
18438061Smsmith
18555939Snsouchstatic devclass_t lp_devclass;
18655939Snsouch
187187576Sjhbstatic int
188187576Sjhblp_module_handler(module_t mod, int what, void *arg)
189187576Sjhb{
190187576Sjhb
191187576Sjhb	switch (what) {
192187576Sjhb	case MOD_UNLOAD:
193187576Sjhb		mtx_lock(&lp_tables_lock);
194187576Sjhb		if (txmith != NULL) {
195187576Sjhb			free(txmith, M_DEVBUF);
196187576Sjhb			txmith = NULL;
197187576Sjhb		}
198187576Sjhb		if (ctxmith != NULL) {
199187576Sjhb			free(ctxmith, M_DEVBUF);
200187576Sjhb			ctxmith = NULL;
201187576Sjhb		}
202187576Sjhb		mtx_unlock(&lp_tables_lock);
203187576Sjhb		break;
204187576Sjhb	case MOD_LOAD:
205187576Sjhb	case MOD_QUIESCE:
206187576Sjhb		break;
207187576Sjhb	default:
208187576Sjhb		return (EOPNOTSUPP);
209187576Sjhb	}
210187576Sjhb	return (0);
211187576Sjhb}
212187576Sjhb
21356455Speterstatic void
21456455Speterlp_identify(driver_t *driver, device_t parent)
21556455Speter{
216127189Sguido	device_t dev;
21755939Snsouch
218155606Sjhb	dev = device_find_child(parent, "plip", -1);
219127189Sguido	if (!dev)
220127189Sguido		BUS_ADD_CHILD(parent, 0, "plip", -1);
22156455Speter}
222184896Sjhb
22355939Snsouchstatic int
22455939Snsouchlp_probe(device_t dev)
22538061Smsmith{
22638061Smsmith
22755939Snsouch	device_set_desc(dev, "PLIP network interface");
22838061Smsmith
22955939Snsouch	return (0);
23038061Smsmith}
23138061Smsmith
23238061Smsmithstatic int
233184896Sjhblp_attach(device_t dev)
23438061Smsmith{
23555939Snsouch	struct lp_data *lp = DEVTOSOFTC(dev);
236147256Sbrooks	struct ifnet *ifp;
237187576Sjhb	int error, rid = 0;
23838061Smsmith
239184130Sjhb	lp->sc_dev = dev;
240184130Sjhb
241183053Sjhb	/*
242183053Sjhb	 * Reserve the interrupt resource.  If we don't have one, the
243183053Sjhb	 * attach fails.
244183053Sjhb	 */
245184896Sjhb	lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
246183053Sjhb	    RF_SHAREABLE);
247183053Sjhb	if (lp->res_irq == 0) {
248183053Sjhb		device_printf(dev, "cannot reserve interrupt, failed.\n");
249183053Sjhb		return (ENXIO);
250183053Sjhb	}
251183053Sjhb
252147256Sbrooks	ifp = lp->sc_ifp = if_alloc(IFT_PARA);
253147256Sbrooks	if (ifp == NULL) {
254147256Sbrooks		return (ENOSPC);
255147256Sbrooks	}
256147256Sbrooks
25755939Snsouch	ifp->if_softc = lp;
258121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
25938061Smsmith	ifp->if_mtu = LPMTU;
260187576Sjhb	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
26138061Smsmith	ifp->if_ioctl = lpioctl;
26238061Smsmith	ifp->if_output = lpoutput;
26338061Smsmith	ifp->if_hdrlen = 0;
26438061Smsmith	ifp->if_addrlen = 0;
265207554Ssobomax	ifp->if_snd.ifq_maxlen = ifqmaxlen;
26638061Smsmith	if_attach(ifp);
26738061Smsmith
26843773Sdes	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
26938061Smsmith
270187576Sjhb	/*
271187576Sjhb	 * Attach our interrupt handler.  It is only called while we
272187576Sjhb	 * own the ppbus.
273187576Sjhb	 */
274187576Sjhb	error = bus_setup_intr(dev, lp->res_irq, INTR_TYPE_NET | INTR_MPSAFE,
275187576Sjhb	    NULL, lp_intr, lp, &lp->sc_intr_cookie);
276187576Sjhb	if (error) {
277187576Sjhb		bpfdetach(ifp);
278187576Sjhb		if_detach(ifp);
279187576Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, lp->res_irq);
280187576Sjhb		device_printf(dev, "Unable to register interrupt handler\n");
281187576Sjhb		return (error);
282187576Sjhb	}
283187576Sjhb
28455939Snsouch	return (0);
28538061Smsmith}
286187576Sjhb
287187576Sjhbstatic int
288187576Sjhblp_detach(device_t dev)
289187576Sjhb{
290187576Sjhb	struct lp_data *sc = device_get_softc(dev);
291187576Sjhb	device_t ppbus = device_get_parent(dev);
292187576Sjhb
293187576Sjhb	ppb_lock(ppbus);
294187576Sjhb	lpstop(sc);
295187576Sjhb	ppb_unlock(ppbus);
296187576Sjhb	bpfdetach(sc->sc_ifp);
297187576Sjhb	if_detach(sc->sc_ifp);
298187576Sjhb	bus_teardown_intr(dev, sc->res_irq, sc->sc_intr_cookie);
299187576Sjhb	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->res_irq);
300187576Sjhb	return (0);
301187576Sjhb}
302187576Sjhb
30338061Smsmith/*
30438061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol.
30538061Smsmith * We don't want to calculate these nasties in our tight loop, so we
30638061Smsmith * precalculate them when we initialize.
30738061Smsmith */
30838061Smsmithstatic int
309184896Sjhblpinittables(void)
31038061Smsmith{
311184896Sjhb	int i;
31238061Smsmith
313187576Sjhb	mtx_lock(&lp_tables_lock);
314184896Sjhb	if (txmith == NULL)
315184896Sjhb		txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
31638061Smsmith
317187576Sjhb	if (txmith == NULL) {
318187576Sjhb		mtx_unlock(&lp_tables_lock);
319184896Sjhb		return (1);
320187576Sjhb	}
32138061Smsmith
322184896Sjhb	if (ctxmith == NULL)
323185003Sjhb		ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
32438061Smsmith
325187576Sjhb	if (ctxmith == NULL) {
326187576Sjhb		mtx_unlock(&lp_tables_lock);
327184896Sjhb		return (1);
328187576Sjhb	}
32938061Smsmith
330184896Sjhb	for (i = 0; i < LPIPTBLSIZE; i++) {
331184896Sjhb		ctxmith[i] = (i & 0xF0) >> 4;
332184896Sjhb		ctxmitl[i] = 0x10 | (i & 0x0F);
333184896Sjhb		ctrecvh[i] = (i & 0x78) << 1;
334184896Sjhb		ctrecvl[i] = (i & 0x78) >> 3;
335184896Sjhb	}
33638061Smsmith
337184896Sjhb	for (i = 0; i < LPIPTBLSIZE; i++) {
338184896Sjhb		txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
339184896Sjhb		txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
340184896Sjhb		trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
341184896Sjhb		trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
342184896Sjhb	}
343187576Sjhb	mtx_unlock(&lp_tables_lock);
34438061Smsmith
345184896Sjhb	return (0);
34638061Smsmith}
34738061Smsmith
348187576Sjhbstatic void
349187576Sjhblpstop(struct lp_data *sc)
350187576Sjhb{
351187576Sjhb	device_t ppbus = device_get_parent(sc->sc_dev);
352187576Sjhb
353187576Sjhb	ppb_assert_locked(ppbus);
354187576Sjhb	ppb_wctr(ppbus, 0x00);
355187576Sjhb	sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
356187576Sjhb	free(sc->sc_ifbuf, M_DEVBUF);
357187576Sjhb	sc->sc_ifbuf = NULL;
358187576Sjhb
359187576Sjhb	/* IFF_UP is not set, try to release the bus anyway */
360187576Sjhb	ppb_release_bus(ppbus, sc->sc_dev);
361187576Sjhb}
362187576Sjhb
363187576Sjhbstatic int
364187576Sjhblpinit_locked(struct ifnet *ifp)
365187576Sjhb{
366187576Sjhb	struct lp_data *sc = ifp->if_softc;
367187576Sjhb	device_t dev = sc->sc_dev;
368187576Sjhb	device_t ppbus = device_get_parent(dev);
369187576Sjhb	int error;
370187576Sjhb
371187576Sjhb	ppb_assert_locked(ppbus);
372187576Sjhb	error = ppb_request_bus(ppbus, dev, PPB_DONTWAIT);
373187576Sjhb	if (error)
374187576Sjhb		return (error);
375187576Sjhb
376187576Sjhb	/* Now IFF_UP means that we own the bus */
377187576Sjhb	ppb_set_mode(ppbus, PPB_COMPATIBLE);
378187576Sjhb
379187576Sjhb	if (lpinittables()) {
380187576Sjhb		ppb_release_bus(ppbus, dev);
381187576Sjhb		return (ENOBUFS);
382187576Sjhb	}
383187576Sjhb
384187576Sjhb	sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN,
385187576Sjhb	    M_DEVBUF, M_NOWAIT);
386187576Sjhb	if (sc->sc_ifbuf == NULL) {
387187576Sjhb		ppb_release_bus(ppbus, dev);
388187576Sjhb		return (ENOBUFS);
389187576Sjhb	}
390187576Sjhb
391187576Sjhb	ppb_wctr(ppbus, IRQENABLE);
392187576Sjhb
393187576Sjhb	ifp->if_drv_flags |= IFF_DRV_RUNNING;
394187576Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
395187576Sjhb	return (0);
396187576Sjhb}
397187576Sjhb
39838061Smsmith/*
39938061Smsmith * Process an ioctl request.
40038061Smsmith */
40138061Smsmithstatic int
402184896Sjhblpioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
40338061Smsmith{
404184896Sjhb	struct lp_data *sc = ifp->if_softc;
405184896Sjhb	device_t dev = sc->sc_dev;
406184896Sjhb	device_t ppbus = device_get_parent(dev);
407184896Sjhb	struct ifaddr *ifa = (struct ifaddr *)data;
408184896Sjhb	struct ifreq *ifr = (struct ifreq *)data;
409184896Sjhb	u_char *ptr;
410184896Sjhb	int error;
41138061Smsmith
412184896Sjhb	switch (cmd) {
413184896Sjhb	case SIOCAIFADDR:
414184896Sjhb	case SIOCSIFADDR:
415184896Sjhb		if (ifa->ifa_addr->sa_family != AF_INET)
416184896Sjhb			return (EAFNOSUPPORT);
41738061Smsmith
418184896Sjhb		ifp->if_flags |= IFF_UP;
419184896Sjhb		/* FALLTHROUGH */
420184896Sjhb	case SIOCSIFFLAGS:
421187576Sjhb		error = 0;
422187576Sjhb		ppb_lock(ppbus);
423184896Sjhb		if ((!(ifp->if_flags & IFF_UP)) &&
424187576Sjhb		    (ifp->if_drv_flags & IFF_DRV_RUNNING))
425187576Sjhb			lpstop(sc);
426187576Sjhb		else if (((ifp->if_flags & IFF_UP)) &&
427187576Sjhb		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING)))
428187576Sjhb			error = lpinit_locked(ifp);
429187576Sjhb		ppb_unlock(ppbus);
430187576Sjhb		return (error);
43138061Smsmith
432187576Sjhb	case SIOCSIFMTU:
433187576Sjhb		ppb_lock(ppbus);
434187576Sjhb		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
435187576Sjhb			ptr = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF,
436187576Sjhb			    M_NOWAIT);
437187576Sjhb			if (ptr == NULL) {
438187576Sjhb				ppb_unlock(ppbus);
439184896Sjhb				return (ENOBUFS);
440184896Sjhb			}
441187576Sjhb			if (sc->sc_ifbuf)
442187576Sjhb				free(sc->sc_ifbuf, M_DEVBUF);
443184896Sjhb			sc->sc_ifbuf = ptr;
444184896Sjhb		}
445184896Sjhb		sc->sc_ifp->if_mtu = ifr->ifr_mtu;
446187576Sjhb		ppb_unlock(ppbus);
447184896Sjhb		break;
44855939Snsouch
449184896Sjhb	case SIOCGIFMTU:
450184896Sjhb		ifr->ifr_mtu = sc->sc_ifp->if_mtu;
451184896Sjhb		break;
45238061Smsmith
453184896Sjhb	case SIOCADDMULTI:
454184896Sjhb	case SIOCDELMULTI:
455184896Sjhb		if (ifr == 0) {
456184896Sjhb			return (EAFNOSUPPORT);		/* XXX */
457184896Sjhb		}
458184896Sjhb		switch (ifr->ifr_addr.sa_family) {
459184896Sjhb		case AF_INET:
460184896Sjhb			break;
461184896Sjhb		default:
462184896Sjhb			return (EAFNOSUPPORT);
463184896Sjhb		}
464184896Sjhb		break;
46538061Smsmith
466184896Sjhb	case SIOCGIFMEDIA:
467184896Sjhb		/*
468184896Sjhb		 * No ifmedia support at this stage; maybe use it
469184896Sjhb		 * in future for eg. protocol selection.
470184896Sjhb		 */
471184896Sjhb		return (EINVAL);
47238061Smsmith
47338061Smsmith	default:
474184896Sjhb		lprintf("LP:ioctl(0x%lx)\n", cmd);
475184896Sjhb		return (EINVAL);
47638061Smsmith	}
477184896Sjhb	return (0);
47838061Smsmith}
47938061Smsmith
48038061Smsmithstatic __inline int
481184896Sjhbclpoutbyte(u_char byte, int spin, device_t ppbus)
48238061Smsmith{
483184896Sjhb
48455939Snsouch	ppb_wdtr(ppbus, ctxmitl[byte]);
48555939Snsouch	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
48638061Smsmith		if (--spin == 0) {
487184896Sjhb			return (1);
48838061Smsmith		}
48955939Snsouch	ppb_wdtr(ppbus, ctxmith[byte]);
49055939Snsouch	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
49138061Smsmith		if (--spin == 0) {
492184896Sjhb			return (1);
49338061Smsmith		}
494184896Sjhb	return (0);
49538061Smsmith}
49638061Smsmith
49738061Smsmithstatic __inline int
498184896Sjhbclpinbyte(int spin, device_t ppbus)
49938061Smsmith{
50042443Snsouch	u_char c, cl;
50138061Smsmith
502187576Sjhb	while ((ppb_rstr(ppbus) & CLPIP_SHAKE))
503184896Sjhb		if (!--spin) {
504184896Sjhb			return (-1);
505184896Sjhb		}
50655939Snsouch	cl = ppb_rstr(ppbus);
50755939Snsouch	ppb_wdtr(ppbus, 0x10);
50838061Smsmith
509187576Sjhb	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
510184896Sjhb		if (!--spin) {
511184896Sjhb			return (-1);
512184896Sjhb		}
51355939Snsouch	c = ppb_rstr(ppbus);
51455939Snsouch	ppb_wdtr(ppbus, 0x00);
51538061Smsmith
51638061Smsmith	return (ctrecvl[cl] | ctrecvh[c]);
51738061Smsmith}
51838061Smsmith
51938061Smsmithstatic void
52043773Sdeslptap(struct ifnet *ifp, struct mbuf *m)
52143773Sdes{
52243773Sdes	u_int32_t af = AF_INET;
523184896Sjhb
524165640Sjhb	bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
52543773Sdes}
52643773Sdes
52743773Sdesstatic void
528184896Sjhblp_intr(void *arg)
52938061Smsmith{
530187576Sjhb	struct lp_data *sc = arg;
531187576Sjhb	device_t ppbus = device_get_parent(sc->sc_dev);
532187576Sjhb	int len, j;
53338061Smsmith	u_char *bp;
53438061Smsmith	u_char c, cl;
53538061Smsmith	struct mbuf *top;
53638061Smsmith
537187576Sjhb	ppb_assert_locked(ppbus);
538147256Sbrooks	if (sc->sc_ifp->if_flags & IFF_LINK0) {
53938061Smsmith
540184896Sjhb		/* Ack. the request */
541184896Sjhb		ppb_wdtr(ppbus, 0x01);
54238061Smsmith
543184896Sjhb		/* Get the packet length */
544184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
545184896Sjhb		if (j == -1)
546184896Sjhb			goto err;
547184896Sjhb		len = j;
548184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
549184896Sjhb		if (j == -1)
550184896Sjhb			goto err;
551184896Sjhb		len = len + (j << 8);
552184896Sjhb		if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN)
553184896Sjhb			goto err;
55438061Smsmith
555184896Sjhb		bp = sc->sc_ifbuf;
55638061Smsmith
557184896Sjhb		while (len--) {
558184896Sjhb			j = clpinbyte(LPMAXSPIN2, ppbus);
559184896Sjhb			if (j == -1) {
560184896Sjhb				goto err;
561184896Sjhb			}
562184896Sjhb			*bp++ = j;
563184896Sjhb		}
56438061Smsmith
565184896Sjhb		/* Get and ignore checksum */
566184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
567184896Sjhb		if (j == -1) {
568184896Sjhb			goto err;
569184896Sjhb		}
57038061Smsmith
571184896Sjhb		len = bp - sc->sc_ifbuf;
572184896Sjhb		if (len <= CLPIPHDRLEN)
573184896Sjhb			goto err;
574184896Sjhb
575184896Sjhb		sc->sc_iferrs = 0;
576184896Sjhb
577184896Sjhb		len -= CLPIPHDRLEN;
578184896Sjhb		sc->sc_ifp->if_ipackets++;
579184896Sjhb		sc->sc_ifp->if_ibytes += len;
580184896Sjhb		top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp,
581184896Sjhb		    0);
582184896Sjhb		if (top) {
583187576Sjhb			ppb_unlock(ppbus);
584184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
585184896Sjhb				lptap(sc->sc_ifp, top);
586184896Sjhb
587223741Sbz			M_SETFIB(top, sc->sc_ifp->if_fib);
588223741Sbz
589184896Sjhb			/* mbuf is free'd on failure. */
590184896Sjhb			netisr_queue(NETISR_IP, top);
591187576Sjhb			ppb_lock(ppbus);
592184896Sjhb		}
593187576Sjhb		return;
59438061Smsmith	}
59555939Snsouch	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
596184896Sjhb		len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
597184896Sjhb		bp  = sc->sc_ifbuf;
598184896Sjhb		while (len--) {
59938061Smsmith
600184896Sjhb			cl = ppb_rstr(ppbus);
601184896Sjhb			ppb_wdtr(ppbus, 8);
60238061Smsmith
603184896Sjhb			j = LPMAXSPIN2;
604187576Sjhb			while ((ppb_rstr(ppbus) & LPIP_SHAKE))
605184896Sjhb				if (!--j)
606184896Sjhb					goto err;
60738061Smsmith
608184896Sjhb			c = ppb_rstr(ppbus);
609184896Sjhb			ppb_wdtr(ppbus, 0);
61038061Smsmith
611184896Sjhb			*bp++= trecvh[cl] | trecvl[c];
61238061Smsmith
613184896Sjhb			j = LPMAXSPIN2;
614184896Sjhb			while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) {
615184896Sjhb				if (cl != c &&
616184896Sjhb				    (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
617184896Sjhb				    (c & 0xf8))
618184896Sjhb					goto end;
619184896Sjhb				if (!--j)
620184896Sjhb					goto err;
621184896Sjhb			}
62238061Smsmith		}
62338061Smsmith
62438061Smsmith	end:
625184896Sjhb		len = bp - sc->sc_ifbuf;
626184896Sjhb		if (len <= LPIPHDRLEN)
627184896Sjhb			goto err;
62838061Smsmith
629184896Sjhb		sc->sc_iferrs = 0;
63038061Smsmith
631184896Sjhb		len -= LPIPHDRLEN;
632184896Sjhb		sc->sc_ifp->if_ipackets++;
633184896Sjhb		sc->sc_ifp->if_ibytes += len;
634184896Sjhb		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp,
635184896Sjhb		    0);
636184896Sjhb		if (top) {
637187576Sjhb			ppb_unlock(ppbus);
638184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
639184896Sjhb				lptap(sc->sc_ifp, top);
640184896Sjhb
641223741Sbz			M_SETFIB(top, sc->sc_ifp->if_fib);
642223741Sbz
643184896Sjhb			/* mbuf is free'd on failure. */
644184896Sjhb			netisr_queue(NETISR_IP, top);
645187576Sjhb			ppb_lock(ppbus);
646184896Sjhb		}
64738061Smsmith	}
648187576Sjhb	return;
64938061Smsmith
650185003Sjhberr:
65155939Snsouch	ppb_wdtr(ppbus, 0);
65238061Smsmith	lprintf("R");
653147256Sbrooks	sc->sc_ifp->if_ierrors++;
65438061Smsmith	sc->sc_iferrs++;
65538061Smsmith
65638061Smsmith	/*
65738061Smsmith	 * We are not able to send receive anything for now,
65838061Smsmith	 * so stop wasting our time
65938061Smsmith	 */
66038061Smsmith	if (sc->sc_iferrs > LPMAXERRS) {
661184896Sjhb		if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n");
662184896Sjhb		ppb_wctr(ppbus, 0x00);
663184896Sjhb		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
664184896Sjhb		sc->sc_iferrs = 0;
66538061Smsmith	}
66638061Smsmith}
66738061Smsmith
66838061Smsmithstatic __inline int
669185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus)
67038061Smsmith{
671184896Sjhb
672184896Sjhb	ppb_wdtr(ppbus, txmith[byte]);
673184896Sjhb	while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
674184896Sjhb		if (--spin == 0)
675184896Sjhb			return (1);
676184896Sjhb	ppb_wdtr(ppbus, txmitl[byte]);
677184896Sjhb	while (ppb_rstr(ppbus) & LPIP_SHAKE)
678184896Sjhb		if (--spin == 0)
679184896Sjhb			return (1);
680184896Sjhb	return (0);
68138061Smsmith}
68238061Smsmith
68338061Smsmithstatic int
684249925Sglebiuslpoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
685191148Skmacy    struct route *ro)
68638061Smsmith{
687184896Sjhb	struct lp_data *sc = ifp->if_softc;
688184896Sjhb	device_t dev = sc->sc_dev;
689184896Sjhb	device_t ppbus = device_get_parent(dev);
690187576Sjhb	int err;
691184896Sjhb	struct mbuf *mm;
692184896Sjhb	u_char *cp = "\0\0";
693184896Sjhb	u_char chksum = 0;
694184896Sjhb	int count = 0;
695184896Sjhb	int i, len, spin;
69638061Smsmith
697184896Sjhb	/* We need a sensible value if we abort */
698184896Sjhb	cp++;
699187576Sjhb	ppb_lock(ppbus);
700187576Sjhb	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
70138061Smsmith
702184896Sjhb	err = 1;		/* assume we're aborting because of an error */
70338061Smsmith
704184896Sjhb	/* Suspend (on laptops) or receive-errors might have taken us offline */
705184896Sjhb	ppb_wctr(ppbus, IRQENABLE);
70638061Smsmith
707184896Sjhb	if (ifp->if_flags & IFF_LINK0) {
708184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
709184896Sjhb			lprintf("&");
710187576Sjhb			lp_intr(sc);
711184896Sjhb		}
71238061Smsmith
713184896Sjhb		/* Alert other end to pending packet */
714184896Sjhb		spin = LPMAXSPIN1;
715184896Sjhb		ppb_wdtr(ppbus, 0x08);
716184896Sjhb		while ((ppb_rstr(ppbus) & 0x08) == 0)
717184896Sjhb			if (--spin == 0) {
718184896Sjhb				goto nend;
719184896Sjhb			}
72038061Smsmith
721184896Sjhb		/* Calculate length of packet, then send that */
722184896Sjhb
723184896Sjhb		count += 14;		/* Ethernet header len */
724184896Sjhb
725184896Sjhb		mm = m;
726184896Sjhb		for (mm = m; mm; mm = mm->m_next) {
727184896Sjhb			count += mm->m_len;
728184896Sjhb		}
729184896Sjhb		if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
73038061Smsmith			goto nend;
731184896Sjhb		if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
732184896Sjhb			goto nend;
733184896Sjhb
734184896Sjhb		/* Send dummy ethernet header */
735184896Sjhb		for (i = 0; i < 12; i++) {
736184896Sjhb			if (clpoutbyte(i, LPMAXSPIN1, ppbus))
737184896Sjhb				goto nend;
738184896Sjhb			chksum += i;
73938061Smsmith		}
74038061Smsmith
741184896Sjhb		if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
742184896Sjhb			goto nend;
743184896Sjhb		if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
744184896Sjhb			goto nend;
745184896Sjhb		chksum += 0x08 + 0x00;		/* Add into checksum */
74638061Smsmith
747184896Sjhb		mm = m;
748184896Sjhb		do {
749184896Sjhb			cp = mtod(mm, u_char *);
750184896Sjhb			len = mm->m_len;
751184896Sjhb			while (len--) {
752184896Sjhb				chksum += *cp;
753184896Sjhb				if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
754184896Sjhb					goto nend;
755184896Sjhb			}
756184896Sjhb		} while ((mm = mm->m_next));
75738061Smsmith
758184896Sjhb		/* Send checksum */
759184896Sjhb		if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
760184896Sjhb			goto nend;
761184896Sjhb
762184896Sjhb		/* Go quiescent */
763184896Sjhb		ppb_wdtr(ppbus, 0);
764184896Sjhb
765184896Sjhb		err = 0;			/* No errors */
766184896Sjhb
767184896Sjhb	nend:
768187576Sjhb		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
769184896Sjhb		if (err)  {			/* if we didn't timeout... */
770184896Sjhb			ifp->if_oerrors++;
771184896Sjhb			lprintf("X");
772184896Sjhb		} else {
773184896Sjhb			ifp->if_opackets++;
774184896Sjhb			ifp->if_obytes += m->m_pkthdr.len;
775184896Sjhb			if (bpf_peers_present(ifp->if_bpf))
776184896Sjhb				lptap(ifp, m);
777184896Sjhb		}
778184896Sjhb
779184896Sjhb		m_freem(m);
780184896Sjhb
781184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
782184896Sjhb			lprintf("^");
783187576Sjhb			lp_intr(sc);
784184896Sjhb		}
785187576Sjhb		ppb_unlock(ppbus);
786184896Sjhb		return (0);
78738061Smsmith	}
78838061Smsmith
789184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
790184896Sjhb		lprintf("&");
791187576Sjhb		lp_intr(sc);
79238061Smsmith	}
79338061Smsmith
794184896Sjhb	if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
795184896Sjhb		goto end;
796184896Sjhb	if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
797184896Sjhb		goto end;
79838061Smsmith
79938061Smsmith	mm = m;
80038061Smsmith	do {
80138061Smsmith		cp = mtod(mm, u_char *);
80243773Sdes		len = mm->m_len;
803184896Sjhb		while (len--)
804184896Sjhb			if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
805184896Sjhb				goto end;
80638061Smsmith	} while ((mm = mm->m_next));
80738061Smsmith
808184896Sjhb	err = 0;			/* no errors were encountered */
80938061Smsmith
810184896Sjhbend:
811184896Sjhb	--cp;
812184896Sjhb	ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
81338061Smsmith
814187576Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
815184896Sjhb	if (err)  {			/* if we didn't timeout... */
81638061Smsmith		ifp->if_oerrors++;
81738061Smsmith		lprintf("X");
81838061Smsmith	} else {
81938061Smsmith		ifp->if_opackets++;
82038061Smsmith		ifp->if_obytes += m->m_pkthdr.len;
821165632Sjhb		if (bpf_peers_present(ifp->if_bpf))
822184896Sjhb			lptap(ifp, m);
82338061Smsmith	}
82438061Smsmith
82538061Smsmith	m_freem(m);
82638061Smsmith
827184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
82838061Smsmith		lprintf("^");
829187576Sjhb		lp_intr(sc);
83038061Smsmith	}
831184896Sjhb
832187576Sjhb	ppb_unlock(ppbus);
833184896Sjhb	return (0);
83438061Smsmith}
83555939Snsouch
83656455Speterstatic device_method_t lp_methods[] = {
83756455Speter  	/* device interface */
83856455Speter	DEVMETHOD(device_identify,	lp_identify),
83956455Speter	DEVMETHOD(device_probe,		lp_probe),
84056455Speter	DEVMETHOD(device_attach,	lp_attach),
841187576Sjhb	DEVMETHOD(device_detach,	lp_detach),
84256455Speter
84356455Speter	{ 0, 0 }
84456455Speter};
84556455Speter
84656455Speterstatic driver_t lp_driver = {
847184896Sjhb	"plip",
848184896Sjhb	lp_methods,
849184896Sjhb	sizeof(struct lp_data),
85056455Speter};
85156455Speter
852187576SjhbDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0);
853153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1);
854