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);
17638061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, 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 SIOCSIFDSTADDR:
414184896Sjhb	case SIOCAIFADDR:
415184896Sjhb	case SIOCSIFADDR:
416184896Sjhb		if (ifa->ifa_addr->sa_family != AF_INET)
417184896Sjhb			return (EAFNOSUPPORT);
41838061Smsmith
419184896Sjhb		ifp->if_flags |= IFF_UP;
420184896Sjhb		/* FALLTHROUGH */
421184896Sjhb	case SIOCSIFFLAGS:
422187576Sjhb		error = 0;
423187576Sjhb		ppb_lock(ppbus);
424184896Sjhb		if ((!(ifp->if_flags & IFF_UP)) &&
425187576Sjhb		    (ifp->if_drv_flags & IFF_DRV_RUNNING))
426187576Sjhb			lpstop(sc);
427187576Sjhb		else if (((ifp->if_flags & IFF_UP)) &&
428187576Sjhb		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING)))
429187576Sjhb			error = lpinit_locked(ifp);
430187576Sjhb		ppb_unlock(ppbus);
431187576Sjhb		return (error);
43238061Smsmith
433187576Sjhb	case SIOCSIFMTU:
434187576Sjhb		ppb_lock(ppbus);
435187576Sjhb		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
436187576Sjhb			ptr = malloc(ifr->ifr_mtu + MLPIPHDRLEN, M_DEVBUF,
437187576Sjhb			    M_NOWAIT);
438187576Sjhb			if (ptr == NULL) {
439187576Sjhb				ppb_unlock(ppbus);
440184896Sjhb				return (ENOBUFS);
441184896Sjhb			}
442187576Sjhb			if (sc->sc_ifbuf)
443187576Sjhb				free(sc->sc_ifbuf, M_DEVBUF);
444184896Sjhb			sc->sc_ifbuf = ptr;
445184896Sjhb		}
446184896Sjhb		sc->sc_ifp->if_mtu = ifr->ifr_mtu;
447187576Sjhb		ppb_unlock(ppbus);
448184896Sjhb		break;
44955939Snsouch
450184896Sjhb	case SIOCGIFMTU:
451184896Sjhb		ifr->ifr_mtu = sc->sc_ifp->if_mtu;
452184896Sjhb		break;
45338061Smsmith
454184896Sjhb	case SIOCADDMULTI:
455184896Sjhb	case SIOCDELMULTI:
456184896Sjhb		if (ifr == 0) {
457184896Sjhb			return (EAFNOSUPPORT);		/* XXX */
458184896Sjhb		}
459184896Sjhb		switch (ifr->ifr_addr.sa_family) {
460184896Sjhb		case AF_INET:
461184896Sjhb			break;
462184896Sjhb		default:
463184896Sjhb			return (EAFNOSUPPORT);
464184896Sjhb		}
465184896Sjhb		break;
46638061Smsmith
467184896Sjhb	case SIOCGIFMEDIA:
468184896Sjhb		/*
469184896Sjhb		 * No ifmedia support at this stage; maybe use it
470184896Sjhb		 * in future for eg. protocol selection.
471184896Sjhb		 */
472184896Sjhb		return (EINVAL);
47338061Smsmith
47438061Smsmith	default:
475184896Sjhb		lprintf("LP:ioctl(0x%lx)\n", cmd);
476184896Sjhb		return (EINVAL);
47738061Smsmith	}
478184896Sjhb	return (0);
47938061Smsmith}
48038061Smsmith
48138061Smsmithstatic __inline int
482184896Sjhbclpoutbyte(u_char byte, int spin, device_t ppbus)
48338061Smsmith{
484184896Sjhb
48555939Snsouch	ppb_wdtr(ppbus, ctxmitl[byte]);
48655939Snsouch	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
48738061Smsmith		if (--spin == 0) {
488184896Sjhb			return (1);
48938061Smsmith		}
49055939Snsouch	ppb_wdtr(ppbus, ctxmith[byte]);
49155939Snsouch	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
49238061Smsmith		if (--spin == 0) {
493184896Sjhb			return (1);
49438061Smsmith		}
495184896Sjhb	return (0);
49638061Smsmith}
49738061Smsmith
49838061Smsmithstatic __inline int
499184896Sjhbclpinbyte(int spin, device_t ppbus)
50038061Smsmith{
50142443Snsouch	u_char c, cl;
50238061Smsmith
503187576Sjhb	while ((ppb_rstr(ppbus) & CLPIP_SHAKE))
504184896Sjhb		if (!--spin) {
505184896Sjhb			return (-1);
506184896Sjhb		}
50755939Snsouch	cl = ppb_rstr(ppbus);
50855939Snsouch	ppb_wdtr(ppbus, 0x10);
50938061Smsmith
510187576Sjhb	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
511184896Sjhb		if (!--spin) {
512184896Sjhb			return (-1);
513184896Sjhb		}
51455939Snsouch	c = ppb_rstr(ppbus);
51555939Snsouch	ppb_wdtr(ppbus, 0x00);
51638061Smsmith
51738061Smsmith	return (ctrecvl[cl] | ctrecvh[c]);
51838061Smsmith}
51938061Smsmith
52038061Smsmithstatic void
52143773Sdeslptap(struct ifnet *ifp, struct mbuf *m)
52243773Sdes{
52343773Sdes	u_int32_t af = AF_INET;
524184896Sjhb
525165640Sjhb	bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
52643773Sdes}
52743773Sdes
52843773Sdesstatic void
529184896Sjhblp_intr(void *arg)
53038061Smsmith{
531187576Sjhb	struct lp_data *sc = arg;
532187576Sjhb	device_t ppbus = device_get_parent(sc->sc_dev);
533187576Sjhb	int len, j;
53438061Smsmith	u_char *bp;
53538061Smsmith	u_char c, cl;
53638061Smsmith	struct mbuf *top;
53738061Smsmith
538187576Sjhb	ppb_assert_locked(ppbus);
539147256Sbrooks	if (sc->sc_ifp->if_flags & IFF_LINK0) {
54038061Smsmith
541184896Sjhb		/* Ack. the request */
542184896Sjhb		ppb_wdtr(ppbus, 0x01);
54338061Smsmith
544184896Sjhb		/* Get the packet length */
545184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
546184896Sjhb		if (j == -1)
547184896Sjhb			goto err;
548184896Sjhb		len = j;
549184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
550184896Sjhb		if (j == -1)
551184896Sjhb			goto err;
552184896Sjhb		len = len + (j << 8);
553184896Sjhb		if (len > sc->sc_ifp->if_mtu + MLPIPHDRLEN)
554184896Sjhb			goto err;
55538061Smsmith
556184896Sjhb		bp = sc->sc_ifbuf;
55738061Smsmith
558184896Sjhb		while (len--) {
559184896Sjhb			j = clpinbyte(LPMAXSPIN2, ppbus);
560184896Sjhb			if (j == -1) {
561184896Sjhb				goto err;
562184896Sjhb			}
563184896Sjhb			*bp++ = j;
564184896Sjhb		}
56538061Smsmith
566184896Sjhb		/* Get and ignore checksum */
567184896Sjhb		j = clpinbyte(LPMAXSPIN2, ppbus);
568184896Sjhb		if (j == -1) {
569184896Sjhb			goto err;
570184896Sjhb		}
57138061Smsmith
572184896Sjhb		len = bp - sc->sc_ifbuf;
573184896Sjhb		if (len <= CLPIPHDRLEN)
574184896Sjhb			goto err;
575184896Sjhb
576184896Sjhb		sc->sc_iferrs = 0;
577184896Sjhb
578184896Sjhb		len -= CLPIPHDRLEN;
579184896Sjhb		sc->sc_ifp->if_ipackets++;
580184896Sjhb		sc->sc_ifp->if_ibytes += len;
581184896Sjhb		top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, sc->sc_ifp,
582184896Sjhb		    0);
583184896Sjhb		if (top) {
584187576Sjhb			ppb_unlock(ppbus);
585184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
586184896Sjhb				lptap(sc->sc_ifp, top);
587184896Sjhb
588223741Sbz			M_SETFIB(top, sc->sc_ifp->if_fib);
589223741Sbz
590184896Sjhb			/* mbuf is free'd on failure. */
591184896Sjhb			netisr_queue(NETISR_IP, top);
592187576Sjhb			ppb_lock(ppbus);
593184896Sjhb		}
594187576Sjhb		return;
59538061Smsmith	}
59655939Snsouch	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
597184896Sjhb		len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
598184896Sjhb		bp  = sc->sc_ifbuf;
599184896Sjhb		while (len--) {
60038061Smsmith
601184896Sjhb			cl = ppb_rstr(ppbus);
602184896Sjhb			ppb_wdtr(ppbus, 8);
60338061Smsmith
604184896Sjhb			j = LPMAXSPIN2;
605187576Sjhb			while ((ppb_rstr(ppbus) & LPIP_SHAKE))
606184896Sjhb				if (!--j)
607184896Sjhb					goto err;
60838061Smsmith
609184896Sjhb			c = ppb_rstr(ppbus);
610184896Sjhb			ppb_wdtr(ppbus, 0);
61138061Smsmith
612184896Sjhb			*bp++= trecvh[cl] | trecvl[c];
61338061Smsmith
614184896Sjhb			j = LPMAXSPIN2;
615184896Sjhb			while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) {
616184896Sjhb				if (cl != c &&
617184896Sjhb				    (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
618184896Sjhb				    (c & 0xf8))
619184896Sjhb					goto end;
620184896Sjhb				if (!--j)
621184896Sjhb					goto err;
622184896Sjhb			}
62338061Smsmith		}
62438061Smsmith
62538061Smsmith	end:
626184896Sjhb		len = bp - sc->sc_ifbuf;
627184896Sjhb		if (len <= LPIPHDRLEN)
628184896Sjhb			goto err;
62938061Smsmith
630184896Sjhb		sc->sc_iferrs = 0;
63138061Smsmith
632184896Sjhb		len -= LPIPHDRLEN;
633184896Sjhb		sc->sc_ifp->if_ipackets++;
634184896Sjhb		sc->sc_ifp->if_ibytes += len;
635184896Sjhb		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp,
636184896Sjhb		    0);
637184896Sjhb		if (top) {
638187576Sjhb			ppb_unlock(ppbus);
639184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
640184896Sjhb				lptap(sc->sc_ifp, top);
641184896Sjhb
642223741Sbz			M_SETFIB(top, sc->sc_ifp->if_fib);
643223741Sbz
644184896Sjhb			/* mbuf is free'd on failure. */
645184896Sjhb			netisr_queue(NETISR_IP, top);
646187576Sjhb			ppb_lock(ppbus);
647184896Sjhb		}
64838061Smsmith	}
649187576Sjhb	return;
65038061Smsmith
651185003Sjhberr:
65255939Snsouch	ppb_wdtr(ppbus, 0);
65338061Smsmith	lprintf("R");
654147256Sbrooks	sc->sc_ifp->if_ierrors++;
65538061Smsmith	sc->sc_iferrs++;
65638061Smsmith
65738061Smsmith	/*
65838061Smsmith	 * We are not able to send receive anything for now,
65938061Smsmith	 * so stop wasting our time
66038061Smsmith	 */
66138061Smsmith	if (sc->sc_iferrs > LPMAXERRS) {
662184896Sjhb		if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n");
663184896Sjhb		ppb_wctr(ppbus, 0x00);
664184896Sjhb		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
665184896Sjhb		sc->sc_iferrs = 0;
66638061Smsmith	}
66738061Smsmith}
66838061Smsmith
66938061Smsmithstatic __inline int
670185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus)
67138061Smsmith{
672184896Sjhb
673184896Sjhb	ppb_wdtr(ppbus, txmith[byte]);
674184896Sjhb	while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
675184896Sjhb		if (--spin == 0)
676184896Sjhb			return (1);
677184896Sjhb	ppb_wdtr(ppbus, txmitl[byte]);
678184896Sjhb	while (ppb_rstr(ppbus) & LPIP_SHAKE)
679184896Sjhb		if (--spin == 0)
680184896Sjhb			return (1);
681184896Sjhb	return (0);
68238061Smsmith}
68338061Smsmith
68438061Smsmithstatic int
685184896Sjhblpoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
686191148Skmacy    struct route *ro)
68738061Smsmith{
688184896Sjhb	struct lp_data *sc = ifp->if_softc;
689184896Sjhb	device_t dev = sc->sc_dev;
690184896Sjhb	device_t ppbus = device_get_parent(dev);
691187576Sjhb	int err;
692184896Sjhb	struct mbuf *mm;
693184896Sjhb	u_char *cp = "\0\0";
694184896Sjhb	u_char chksum = 0;
695184896Sjhb	int count = 0;
696184896Sjhb	int i, len, spin;
69738061Smsmith
698184896Sjhb	/* We need a sensible value if we abort */
699184896Sjhb	cp++;
700187576Sjhb	ppb_lock(ppbus);
701187576Sjhb	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
70238061Smsmith
703184896Sjhb	err = 1;		/* assume we're aborting because of an error */
70438061Smsmith
705184896Sjhb	/* Suspend (on laptops) or receive-errors might have taken us offline */
706184896Sjhb	ppb_wctr(ppbus, IRQENABLE);
70738061Smsmith
708184896Sjhb	if (ifp->if_flags & IFF_LINK0) {
709184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
710184896Sjhb			lprintf("&");
711187576Sjhb			lp_intr(sc);
712184896Sjhb		}
71338061Smsmith
714184896Sjhb		/* Alert other end to pending packet */
715184896Sjhb		spin = LPMAXSPIN1;
716184896Sjhb		ppb_wdtr(ppbus, 0x08);
717184896Sjhb		while ((ppb_rstr(ppbus) & 0x08) == 0)
718184896Sjhb			if (--spin == 0) {
719184896Sjhb				goto nend;
720184896Sjhb			}
72138061Smsmith
722184896Sjhb		/* Calculate length of packet, then send that */
723184896Sjhb
724184896Sjhb		count += 14;		/* Ethernet header len */
725184896Sjhb
726184896Sjhb		mm = m;
727184896Sjhb		for (mm = m; mm; mm = mm->m_next) {
728184896Sjhb			count += mm->m_len;
729184896Sjhb		}
730184896Sjhb		if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
73138061Smsmith			goto nend;
732184896Sjhb		if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
733184896Sjhb			goto nend;
734184896Sjhb
735184896Sjhb		/* Send dummy ethernet header */
736184896Sjhb		for (i = 0; i < 12; i++) {
737184896Sjhb			if (clpoutbyte(i, LPMAXSPIN1, ppbus))
738184896Sjhb				goto nend;
739184896Sjhb			chksum += i;
74038061Smsmith		}
74138061Smsmith
742184896Sjhb		if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
743184896Sjhb			goto nend;
744184896Sjhb		if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
745184896Sjhb			goto nend;
746184896Sjhb		chksum += 0x08 + 0x00;		/* Add into checksum */
74738061Smsmith
748184896Sjhb		mm = m;
749184896Sjhb		do {
750184896Sjhb			cp = mtod(mm, u_char *);
751184896Sjhb			len = mm->m_len;
752184896Sjhb			while (len--) {
753184896Sjhb				chksum += *cp;
754184896Sjhb				if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
755184896Sjhb					goto nend;
756184896Sjhb			}
757184896Sjhb		} while ((mm = mm->m_next));
75838061Smsmith
759184896Sjhb		/* Send checksum */
760184896Sjhb		if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
761184896Sjhb			goto nend;
762184896Sjhb
763184896Sjhb		/* Go quiescent */
764184896Sjhb		ppb_wdtr(ppbus, 0);
765184896Sjhb
766184896Sjhb		err = 0;			/* No errors */
767184896Sjhb
768184896Sjhb	nend:
769187576Sjhb		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
770184896Sjhb		if (err)  {			/* if we didn't timeout... */
771184896Sjhb			ifp->if_oerrors++;
772184896Sjhb			lprintf("X");
773184896Sjhb		} else {
774184896Sjhb			ifp->if_opackets++;
775184896Sjhb			ifp->if_obytes += m->m_pkthdr.len;
776184896Sjhb			if (bpf_peers_present(ifp->if_bpf))
777184896Sjhb				lptap(ifp, m);
778184896Sjhb		}
779184896Sjhb
780184896Sjhb		m_freem(m);
781184896Sjhb
782184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
783184896Sjhb			lprintf("^");
784187576Sjhb			lp_intr(sc);
785184896Sjhb		}
786187576Sjhb		ppb_unlock(ppbus);
787184896Sjhb		return (0);
78838061Smsmith	}
78938061Smsmith
790184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
791184896Sjhb		lprintf("&");
792187576Sjhb		lp_intr(sc);
79338061Smsmith	}
79438061Smsmith
795184896Sjhb	if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
796184896Sjhb		goto end;
797184896Sjhb	if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
798184896Sjhb		goto end;
79938061Smsmith
80038061Smsmith	mm = m;
80138061Smsmith	do {
80238061Smsmith		cp = mtod(mm, u_char *);
80343773Sdes		len = mm->m_len;
804184896Sjhb		while (len--)
805184896Sjhb			if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
806184896Sjhb				goto end;
80738061Smsmith	} while ((mm = mm->m_next));
80838061Smsmith
809184896Sjhb	err = 0;			/* no errors were encountered */
81038061Smsmith
811184896Sjhbend:
812184896Sjhb	--cp;
813184896Sjhb	ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
81438061Smsmith
815187576Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
816184896Sjhb	if (err)  {			/* if we didn't timeout... */
81738061Smsmith		ifp->if_oerrors++;
81838061Smsmith		lprintf("X");
81938061Smsmith	} else {
82038061Smsmith		ifp->if_opackets++;
82138061Smsmith		ifp->if_obytes += m->m_pkthdr.len;
822165632Sjhb		if (bpf_peers_present(ifp->if_bpf))
823184896Sjhb			lptap(ifp, m);
82438061Smsmith	}
82538061Smsmith
82638061Smsmith	m_freem(m);
82738061Smsmith
828184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
82938061Smsmith		lprintf("^");
830187576Sjhb		lp_intr(sc);
83138061Smsmith	}
832184896Sjhb
833187576Sjhb	ppb_unlock(ppbus);
834184896Sjhb	return (0);
83538061Smsmith}
83655939Snsouch
83756455Speterstatic device_method_t lp_methods[] = {
83856455Speter  	/* device interface */
83956455Speter	DEVMETHOD(device_identify,	lp_identify),
84056455Speter	DEVMETHOD(device_probe,		lp_probe),
84156455Speter	DEVMETHOD(device_attach,	lp_attach),
842187576Sjhb	DEVMETHOD(device_detach,	lp_detach),
84356455Speter
84456455Speter	{ 0, 0 }
84556455Speter};
84656455Speter
84756455Speterstatic driver_t lp_driver = {
848184896Sjhb	"plip",
849184896Sjhb	lp_methods,
850184896Sjhb	sizeof(struct lp_data),
85156455Speter};
85256455Speter
853187576SjhbDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0);
854153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1);
855