if_plip.c revision 187576
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: head/sys/dev/ppbus/if_plip.c 187576 2009-01-21 23:10:06Z jhb $");
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>
10238061Smsmith
10338061Smsmith#include <netinet/in.h>
10438061Smsmith#include <netinet/in_var.h>
10538061Smsmith
10638061Smsmith#include <net/bpf.h>
10738061Smsmith
10838061Smsmith#include <dev/ppbus/ppbconf.h>
10955939Snsouch#include "ppbus_if.h"
11055939Snsouch#include <dev/ppbus/ppbio.h>
11138061Smsmith
11238061Smsmith#ifndef LPMTU			/* MTU for the lp# interfaces */
113184896Sjhb#define	LPMTU		1500
11438061Smsmith#endif
11538061Smsmith
11638061Smsmith#ifndef LPMAXSPIN1		/* DELAY factor for the lp# interfaces */
11738061Smsmith#define	LPMAXSPIN1	8000   /* Spinning for remote intr to happen */
11838061Smsmith#endif
11938061Smsmith
12038061Smsmith#ifndef LPMAXSPIN2		/* DELAY factor for the lp# interfaces */
12138061Smsmith#define	LPMAXSPIN2	500	/* Spinning for remote handshake to happen */
12238061Smsmith#endif
12338061Smsmith
12438061Smsmith#ifndef LPMAXERRS		/* Max errors before !RUNNING */
12538061Smsmith#define	LPMAXERRS	100
12638061Smsmith#endif
12738061Smsmith
128185003Sjhb#define	CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
12938061Smsmith#define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
130185003Sjhb#define	MLPIPHDRLEN	CLPIPHDRLEN
13138061Smsmith
132185003Sjhb#define	LPIPHDRLEN	2	/* We send 0x08, 0x00 in front of packet */
13338061Smsmith#define	LPIP_SHAKE	0x40	/* This bit toggles between nibble reception */
13438061Smsmith#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
135185003Sjhb#define	MLPIPHDRLEN	LPIPHDRLEN
13638061Smsmith#endif
13738061Smsmith
13838061Smsmith#define	LPIPTBLSIZE	256	/* Size of octet translation table */
13938061Smsmith
140185003Sjhb#define	lprintf		if (lptflag) printf
14143433Snsouch
14243433Snsouch#ifdef PLIP_DEBUG
14338061Smsmithstatic int volatile lptflag = 1;
14443433Snsouch#else
14543433Snsouchstatic int volatile lptflag = 0;
14638061Smsmith#endif
14738061Smsmith
14855939Snsouchstruct lp_data {
149147256Sbrooks	struct  ifnet	*sc_ifp;
150184130Sjhb	device_t	sc_dev;
15138061Smsmith	u_char		*sc_ifbuf;
15238061Smsmith	int		sc_iferrs;
15355939Snsouch
15455939Snsouch	struct resource *res_irq;
155187576Sjhb	void		*sc_intr_cookie;
15638061Smsmith};
15738061Smsmith
158187576Sjhbstatic struct mtx lp_tables_lock;
159187576SjhbMTX_SYSINIT(lp_tables, &lp_tables_lock, "plip tables", MTX_DEF);
160187576Sjhb
16138061Smsmith/* Tables for the lp# interface */
16238061Smsmithstatic u_char *txmith;
163185003Sjhb#define	txmitl (txmith + (1 * LPIPTBLSIZE))
164185003Sjhb#define	trecvh (txmith + (2 * LPIPTBLSIZE))
165185003Sjhb#define	trecvl (txmith + (3 * LPIPTBLSIZE))
16638061Smsmith
16738061Smsmithstatic u_char *ctxmith;
168185003Sjhb#define	ctxmitl (ctxmith + (1 * LPIPTBLSIZE))
169185003Sjhb#define	ctrecvh (ctxmith + (2 * LPIPTBLSIZE))
170185003Sjhb#define	ctrecvl (ctxmith + (3 * LPIPTBLSIZE))
17138061Smsmith
17238061Smsmith/* Functions for the lp# interface */
17338061Smsmithstatic int lpinittables(void);
17438061Smsmithstatic int lpioctl(struct ifnet *, u_long, caddr_t);
17538061Smsmithstatic int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
17638061Smsmith	struct rtentry *);
177187576Sjhbstatic void lpstop(struct lp_data *);
17855939Snsouchstatic void lp_intr(void *);
179187576Sjhbstatic int lp_module_handler(module_t, int, void *);
18038061Smsmith
181185003Sjhb#define	DEVTOSOFTC(dev) \
18255939Snsouch	((struct lp_data *)device_get_softc(dev))
18338061Smsmith
18455939Snsouchstatic devclass_t lp_devclass;
18555939Snsouch
186187576Sjhbstatic int
187187576Sjhblp_module_handler(module_t mod, int what, void *arg)
188187576Sjhb{
189187576Sjhb
190187576Sjhb	switch (what) {
191187576Sjhb	case MOD_UNLOAD:
192187576Sjhb		mtx_lock(&lp_tables_lock);
193187576Sjhb		if (txmith != NULL) {
194187576Sjhb			free(txmith, M_DEVBUF);
195187576Sjhb			txmith = NULL;
196187576Sjhb		}
197187576Sjhb		if (ctxmith != NULL) {
198187576Sjhb			free(ctxmith, M_DEVBUF);
199187576Sjhb			ctxmith = NULL;
200187576Sjhb		}
201187576Sjhb		mtx_unlock(&lp_tables_lock);
202187576Sjhb		break;
203187576Sjhb	case MOD_LOAD:
204187576Sjhb	case MOD_QUIESCE:
205187576Sjhb		break;
206187576Sjhb	default:
207187576Sjhb		return (EOPNOTSUPP);
208187576Sjhb	}
209187576Sjhb	return (0);
210187576Sjhb}
211187576Sjhb
21256455Speterstatic void
21356455Speterlp_identify(driver_t *driver, device_t parent)
21456455Speter{
215127189Sguido	device_t dev;
21655939Snsouch
217155606Sjhb	dev = device_find_child(parent, "plip", -1);
218127189Sguido	if (!dev)
219127189Sguido		BUS_ADD_CHILD(parent, 0, "plip", -1);
22056455Speter}
221184896Sjhb
22255939Snsouchstatic int
22355939Snsouchlp_probe(device_t dev)
22438061Smsmith{
22538061Smsmith
22655939Snsouch	device_set_desc(dev, "PLIP network interface");
22738061Smsmith
22855939Snsouch	return (0);
22938061Smsmith}
23038061Smsmith
23138061Smsmithstatic int
232184896Sjhblp_attach(device_t dev)
23338061Smsmith{
23455939Snsouch	struct lp_data *lp = DEVTOSOFTC(dev);
235147256Sbrooks	struct ifnet *ifp;
236187576Sjhb	int error, rid = 0;
23738061Smsmith
238184130Sjhb	lp->sc_dev = dev;
239184130Sjhb
240183053Sjhb	/*
241183053Sjhb	 * Reserve the interrupt resource.  If we don't have one, the
242183053Sjhb	 * attach fails.
243183053Sjhb	 */
244184896Sjhb	lp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
245183053Sjhb	    RF_SHAREABLE);
246183053Sjhb	if (lp->res_irq == 0) {
247183053Sjhb		device_printf(dev, "cannot reserve interrupt, failed.\n");
248183053Sjhb		return (ENXIO);
249183053Sjhb	}
250183053Sjhb
251147256Sbrooks	ifp = lp->sc_ifp = if_alloc(IFT_PARA);
252147256Sbrooks	if (ifp == NULL) {
253147256Sbrooks		return (ENOSPC);
254147256Sbrooks	}
255147256Sbrooks
25655939Snsouch	ifp->if_softc = lp;
257121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
25838061Smsmith	ifp->if_mtu = LPMTU;
259187576Sjhb	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
26038061Smsmith	ifp->if_ioctl = lpioctl;
26138061Smsmith	ifp->if_output = lpoutput;
26238061Smsmith	ifp->if_hdrlen = 0;
26338061Smsmith	ifp->if_addrlen = 0;
26438061Smsmith	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
26538061Smsmith	if_attach(ifp);
26638061Smsmith
26743773Sdes	bpfattach(ifp, DLT_NULL, sizeof(u_int32_t));
26838061Smsmith
269187576Sjhb	/*
270187576Sjhb	 * Attach our interrupt handler.  It is only called while we
271187576Sjhb	 * own the ppbus.
272187576Sjhb	 */
273187576Sjhb	error = bus_setup_intr(dev, lp->res_irq, INTR_TYPE_NET | INTR_MPSAFE,
274187576Sjhb	    NULL, lp_intr, lp, &lp->sc_intr_cookie);
275187576Sjhb	if (error) {
276187576Sjhb		bpfdetach(ifp);
277187576Sjhb		if_detach(ifp);
278187576Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, lp->res_irq);
279187576Sjhb		device_printf(dev, "Unable to register interrupt handler\n");
280187576Sjhb		return (error);
281187576Sjhb	}
282187576Sjhb
28355939Snsouch	return (0);
28438061Smsmith}
285187576Sjhb
286187576Sjhbstatic int
287187576Sjhblp_detach(device_t dev)
288187576Sjhb{
289187576Sjhb	struct lp_data *sc = device_get_softc(dev);
290187576Sjhb	device_t ppbus = device_get_parent(dev);
291187576Sjhb
292187576Sjhb	ppb_lock(ppbus);
293187576Sjhb	lpstop(sc);
294187576Sjhb	ppb_unlock(ppbus);
295187576Sjhb	bpfdetach(sc->sc_ifp);
296187576Sjhb	if_detach(sc->sc_ifp);
297187576Sjhb	bus_teardown_intr(dev, sc->res_irq, sc->sc_intr_cookie);
298187576Sjhb	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->res_irq);
299187576Sjhb	return (0);
300187576Sjhb}
301187576Sjhb
30238061Smsmith/*
30338061Smsmith * Build the translation tables for the LPIP (BSD unix) protocol.
30438061Smsmith * We don't want to calculate these nasties in our tight loop, so we
30538061Smsmith * precalculate them when we initialize.
30638061Smsmith */
30738061Smsmithstatic int
308184896Sjhblpinittables(void)
30938061Smsmith{
310184896Sjhb	int i;
31138061Smsmith
312187576Sjhb	mtx_lock(&lp_tables_lock);
313184896Sjhb	if (txmith == NULL)
314184896Sjhb		txmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
31538061Smsmith
316187576Sjhb	if (txmith == NULL) {
317187576Sjhb		mtx_unlock(&lp_tables_lock);
318184896Sjhb		return (1);
319187576Sjhb	}
32038061Smsmith
321184896Sjhb	if (ctxmith == NULL)
322185003Sjhb		ctxmith = malloc(4 * LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
32338061Smsmith
324187576Sjhb	if (ctxmith == NULL) {
325187576Sjhb		mtx_unlock(&lp_tables_lock);
326184896Sjhb		return (1);
327187576Sjhb	}
32838061Smsmith
329184896Sjhb	for (i = 0; i < LPIPTBLSIZE; i++) {
330184896Sjhb		ctxmith[i] = (i & 0xF0) >> 4;
331184896Sjhb		ctxmitl[i] = 0x10 | (i & 0x0F);
332184896Sjhb		ctrecvh[i] = (i & 0x78) << 1;
333184896Sjhb		ctrecvl[i] = (i & 0x78) >> 3;
334184896Sjhb	}
33538061Smsmith
336184896Sjhb	for (i = 0; i < LPIPTBLSIZE; i++) {
337184896Sjhb		txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
338184896Sjhb		txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
339184896Sjhb		trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
340184896Sjhb		trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
341184896Sjhb	}
342187576Sjhb	mtx_unlock(&lp_tables_lock);
34338061Smsmith
344184896Sjhb	return (0);
34538061Smsmith}
34638061Smsmith
347187576Sjhbstatic void
348187576Sjhblpstop(struct lp_data *sc)
349187576Sjhb{
350187576Sjhb	device_t ppbus = device_get_parent(sc->sc_dev);
351187576Sjhb
352187576Sjhb	ppb_assert_locked(ppbus);
353187576Sjhb	ppb_wctr(ppbus, 0x00);
354187576Sjhb	sc->sc_ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
355187576Sjhb	free(sc->sc_ifbuf, M_DEVBUF);
356187576Sjhb	sc->sc_ifbuf = NULL;
357187576Sjhb
358187576Sjhb	/* IFF_UP is not set, try to release the bus anyway */
359187576Sjhb	ppb_release_bus(ppbus, sc->sc_dev);
360187576Sjhb}
361187576Sjhb
362187576Sjhbstatic int
363187576Sjhblpinit_locked(struct ifnet *ifp)
364187576Sjhb{
365187576Sjhb	struct lp_data *sc = ifp->if_softc;
366187576Sjhb	device_t dev = sc->sc_dev;
367187576Sjhb	device_t ppbus = device_get_parent(dev);
368187576Sjhb	int error;
369187576Sjhb
370187576Sjhb	ppb_assert_locked(ppbus);
371187576Sjhb	error = ppb_request_bus(ppbus, dev, PPB_DONTWAIT);
372187576Sjhb	if (error)
373187576Sjhb		return (error);
374187576Sjhb
375187576Sjhb	/* Now IFF_UP means that we own the bus */
376187576Sjhb	ppb_set_mode(ppbus, PPB_COMPATIBLE);
377187576Sjhb
378187576Sjhb	if (lpinittables()) {
379187576Sjhb		ppb_release_bus(ppbus, dev);
380187576Sjhb		return (ENOBUFS);
381187576Sjhb	}
382187576Sjhb
383187576Sjhb	sc->sc_ifbuf = malloc(sc->sc_ifp->if_mtu + MLPIPHDRLEN,
384187576Sjhb	    M_DEVBUF, M_NOWAIT);
385187576Sjhb	if (sc->sc_ifbuf == NULL) {
386187576Sjhb		ppb_release_bus(ppbus, dev);
387187576Sjhb		return (ENOBUFS);
388187576Sjhb	}
389187576Sjhb
390187576Sjhb	ppb_wctr(ppbus, IRQENABLE);
391187576Sjhb
392187576Sjhb	ifp->if_drv_flags |= IFF_DRV_RUNNING;
393187576Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
394187576Sjhb	return (0);
395187576Sjhb}
396187576Sjhb
39738061Smsmith/*
39838061Smsmith * Process an ioctl request.
39938061Smsmith */
40038061Smsmithstatic int
401184896Sjhblpioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
40238061Smsmith{
403184896Sjhb	struct lp_data *sc = ifp->if_softc;
404184896Sjhb	device_t dev = sc->sc_dev;
405184896Sjhb	device_t ppbus = device_get_parent(dev);
406184896Sjhb	struct ifaddr *ifa = (struct ifaddr *)data;
407184896Sjhb	struct ifreq *ifr = (struct ifreq *)data;
408184896Sjhb	u_char *ptr;
409184896Sjhb	int error;
41038061Smsmith
411184896Sjhb	switch (cmd) {
412184896Sjhb	case SIOCSIFDSTADDR:
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
587184896Sjhb			/* mbuf is free'd on failure. */
588184896Sjhb			netisr_queue(NETISR_IP, top);
589187576Sjhb			ppb_lock(ppbus);
590184896Sjhb		}
591187576Sjhb		return;
59238061Smsmith	}
59355939Snsouch	while ((ppb_rstr(ppbus) & LPIP_SHAKE)) {
594184896Sjhb		len = sc->sc_ifp->if_mtu + LPIPHDRLEN;
595184896Sjhb		bp  = sc->sc_ifbuf;
596184896Sjhb		while (len--) {
59738061Smsmith
598184896Sjhb			cl = ppb_rstr(ppbus);
599184896Sjhb			ppb_wdtr(ppbus, 8);
60038061Smsmith
601184896Sjhb			j = LPMAXSPIN2;
602187576Sjhb			while ((ppb_rstr(ppbus) & LPIP_SHAKE))
603184896Sjhb				if (!--j)
604184896Sjhb					goto err;
60538061Smsmith
606184896Sjhb			c = ppb_rstr(ppbus);
607184896Sjhb			ppb_wdtr(ppbus, 0);
60838061Smsmith
609184896Sjhb			*bp++= trecvh[cl] | trecvl[c];
61038061Smsmith
611184896Sjhb			j = LPMAXSPIN2;
612184896Sjhb			while (!((cl = ppb_rstr(ppbus)) & LPIP_SHAKE)) {
613184896Sjhb				if (cl != c &&
614184896Sjhb				    (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
615184896Sjhb				    (c & 0xf8))
616184896Sjhb					goto end;
617184896Sjhb				if (!--j)
618184896Sjhb					goto err;
619184896Sjhb			}
62038061Smsmith		}
62138061Smsmith
62238061Smsmith	end:
623184896Sjhb		len = bp - sc->sc_ifbuf;
624184896Sjhb		if (len <= LPIPHDRLEN)
625184896Sjhb			goto err;
62638061Smsmith
627184896Sjhb		sc->sc_iferrs = 0;
62838061Smsmith
629184896Sjhb		len -= LPIPHDRLEN;
630184896Sjhb		sc->sc_ifp->if_ipackets++;
631184896Sjhb		sc->sc_ifp->if_ibytes += len;
632184896Sjhb		top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, sc->sc_ifp,
633184896Sjhb		    0);
634184896Sjhb		if (top) {
635187576Sjhb			ppb_unlock(ppbus);
636184896Sjhb			if (bpf_peers_present(sc->sc_ifp->if_bpf))
637184896Sjhb				lptap(sc->sc_ifp, top);
638184896Sjhb
639184896Sjhb			/* mbuf is free'd on failure. */
640184896Sjhb			netisr_queue(NETISR_IP, top);
641187576Sjhb			ppb_lock(ppbus);
642184896Sjhb		}
64338061Smsmith	}
644187576Sjhb	return;
64538061Smsmith
646185003Sjhberr:
64755939Snsouch	ppb_wdtr(ppbus, 0);
64838061Smsmith	lprintf("R");
649147256Sbrooks	sc->sc_ifp->if_ierrors++;
65038061Smsmith	sc->sc_iferrs++;
65138061Smsmith
65238061Smsmith	/*
65338061Smsmith	 * We are not able to send receive anything for now,
65438061Smsmith	 * so stop wasting our time
65538061Smsmith	 */
65638061Smsmith	if (sc->sc_iferrs > LPMAXERRS) {
657184896Sjhb		if_printf(sc->sc_ifp, "Too many errors, Going off-line.\n");
658184896Sjhb		ppb_wctr(ppbus, 0x00);
659184896Sjhb		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
660184896Sjhb		sc->sc_iferrs = 0;
66138061Smsmith	}
66238061Smsmith}
66338061Smsmith
66438061Smsmithstatic __inline int
665185003Sjhblpoutbyte(u_char byte, int spin, device_t ppbus)
66638061Smsmith{
667184896Sjhb
668184896Sjhb	ppb_wdtr(ppbus, txmith[byte]);
669184896Sjhb	while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
670184896Sjhb		if (--spin == 0)
671184896Sjhb			return (1);
672184896Sjhb	ppb_wdtr(ppbus, txmitl[byte]);
673184896Sjhb	while (ppb_rstr(ppbus) & LPIP_SHAKE)
674184896Sjhb		if (--spin == 0)
675184896Sjhb			return (1);
676184896Sjhb	return (0);
67738061Smsmith}
67838061Smsmith
67938061Smsmithstatic int
680184896Sjhblpoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
681184896Sjhb    struct rtentry *rt)
68238061Smsmith{
683184896Sjhb	struct lp_data *sc = ifp->if_softc;
684184896Sjhb	device_t dev = sc->sc_dev;
685184896Sjhb	device_t ppbus = device_get_parent(dev);
686187576Sjhb	int err;
687184896Sjhb	struct mbuf *mm;
688184896Sjhb	u_char *cp = "\0\0";
689184896Sjhb	u_char chksum = 0;
690184896Sjhb	int count = 0;
691184896Sjhb	int i, len, spin;
69238061Smsmith
693184896Sjhb	/* We need a sensible value if we abort */
694184896Sjhb	cp++;
695187576Sjhb	ppb_lock(ppbus);
696187576Sjhb	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
69738061Smsmith
698184896Sjhb	err = 1;		/* assume we're aborting because of an error */
69938061Smsmith
700184896Sjhb	/* Suspend (on laptops) or receive-errors might have taken us offline */
701184896Sjhb	ppb_wctr(ppbus, IRQENABLE);
70238061Smsmith
703184896Sjhb	if (ifp->if_flags & IFF_LINK0) {
704184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
705184896Sjhb			lprintf("&");
706187576Sjhb			lp_intr(sc);
707184896Sjhb		}
70838061Smsmith
709184896Sjhb		/* Alert other end to pending packet */
710184896Sjhb		spin = LPMAXSPIN1;
711184896Sjhb		ppb_wdtr(ppbus, 0x08);
712184896Sjhb		while ((ppb_rstr(ppbus) & 0x08) == 0)
713184896Sjhb			if (--spin == 0) {
714184896Sjhb				goto nend;
715184896Sjhb			}
71638061Smsmith
717184896Sjhb		/* Calculate length of packet, then send that */
718184896Sjhb
719184896Sjhb		count += 14;		/* Ethernet header len */
720184896Sjhb
721184896Sjhb		mm = m;
722184896Sjhb		for (mm = m; mm; mm = mm->m_next) {
723184896Sjhb			count += mm->m_len;
724184896Sjhb		}
725184896Sjhb		if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus))
72638061Smsmith			goto nend;
727184896Sjhb		if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus))
728184896Sjhb			goto nend;
729184896Sjhb
730184896Sjhb		/* Send dummy ethernet header */
731184896Sjhb		for (i = 0; i < 12; i++) {
732184896Sjhb			if (clpoutbyte(i, LPMAXSPIN1, ppbus))
733184896Sjhb				goto nend;
734184896Sjhb			chksum += i;
73538061Smsmith		}
73638061Smsmith
737184896Sjhb		if (clpoutbyte(0x08, LPMAXSPIN1, ppbus))
738184896Sjhb			goto nend;
739184896Sjhb		if (clpoutbyte(0x00, LPMAXSPIN1, ppbus))
740184896Sjhb			goto nend;
741184896Sjhb		chksum += 0x08 + 0x00;		/* Add into checksum */
74238061Smsmith
743184896Sjhb		mm = m;
744184896Sjhb		do {
745184896Sjhb			cp = mtod(mm, u_char *);
746184896Sjhb			len = mm->m_len;
747184896Sjhb			while (len--) {
748184896Sjhb				chksum += *cp;
749184896Sjhb				if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus))
750184896Sjhb					goto nend;
751184896Sjhb			}
752184896Sjhb		} while ((mm = mm->m_next));
75338061Smsmith
754184896Sjhb		/* Send checksum */
755184896Sjhb		if (clpoutbyte(chksum, LPMAXSPIN2, ppbus))
756184896Sjhb			goto nend;
757184896Sjhb
758184896Sjhb		/* Go quiescent */
759184896Sjhb		ppb_wdtr(ppbus, 0);
760184896Sjhb
761184896Sjhb		err = 0;			/* No errors */
762184896Sjhb
763184896Sjhb	nend:
764187576Sjhb		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
765184896Sjhb		if (err)  {			/* if we didn't timeout... */
766184896Sjhb			ifp->if_oerrors++;
767184896Sjhb			lprintf("X");
768184896Sjhb		} else {
769184896Sjhb			ifp->if_opackets++;
770184896Sjhb			ifp->if_obytes += m->m_pkthdr.len;
771184896Sjhb			if (bpf_peers_present(ifp->if_bpf))
772184896Sjhb				lptap(ifp, m);
773184896Sjhb		}
774184896Sjhb
775184896Sjhb		m_freem(m);
776184896Sjhb
777184896Sjhb		if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) {
778184896Sjhb			lprintf("^");
779187576Sjhb			lp_intr(sc);
780184896Sjhb		}
781187576Sjhb		ppb_unlock(ppbus);
782184896Sjhb		return (0);
78338061Smsmith	}
78438061Smsmith
785184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
786184896Sjhb		lprintf("&");
787187576Sjhb		lp_intr(sc);
78838061Smsmith	}
78938061Smsmith
790184896Sjhb	if (lpoutbyte(0x08, LPMAXSPIN1, ppbus))
791184896Sjhb		goto end;
792184896Sjhb	if (lpoutbyte(0x00, LPMAXSPIN2, ppbus))
793184896Sjhb		goto end;
79438061Smsmith
79538061Smsmith	mm = m;
79638061Smsmith	do {
79738061Smsmith		cp = mtod(mm, u_char *);
79843773Sdes		len = mm->m_len;
799184896Sjhb		while (len--)
800184896Sjhb			if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus))
801184896Sjhb				goto end;
80238061Smsmith	} while ((mm = mm->m_next));
80338061Smsmith
804184896Sjhb	err = 0;			/* no errors were encountered */
80538061Smsmith
806184896Sjhbend:
807184896Sjhb	--cp;
808184896Sjhb	ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17);
80938061Smsmith
810187576Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
811184896Sjhb	if (err)  {			/* if we didn't timeout... */
81238061Smsmith		ifp->if_oerrors++;
81338061Smsmith		lprintf("X");
81438061Smsmith	} else {
81538061Smsmith		ifp->if_opackets++;
81638061Smsmith		ifp->if_obytes += m->m_pkthdr.len;
817165632Sjhb		if (bpf_peers_present(ifp->if_bpf))
818184896Sjhb			lptap(ifp, m);
81938061Smsmith	}
82038061Smsmith
82138061Smsmith	m_freem(m);
82238061Smsmith
823184896Sjhb	if (ppb_rstr(ppbus) & LPIP_SHAKE) {
82438061Smsmith		lprintf("^");
825187576Sjhb		lp_intr(sc);
82638061Smsmith	}
827184896Sjhb
828187576Sjhb	ppb_unlock(ppbus);
829184896Sjhb	return (0);
83038061Smsmith}
83155939Snsouch
83256455Speterstatic device_method_t lp_methods[] = {
83356455Speter  	/* device interface */
83456455Speter	DEVMETHOD(device_identify,	lp_identify),
83556455Speter	DEVMETHOD(device_probe,		lp_probe),
83656455Speter	DEVMETHOD(device_attach,	lp_attach),
837187576Sjhb	DEVMETHOD(device_detach,	lp_detach),
83856455Speter
83956455Speter	{ 0, 0 }
84056455Speter};
84156455Speter
84256455Speterstatic driver_t lp_driver = {
843184896Sjhb	"plip",
844184896Sjhb	lp_methods,
845184896Sjhb	sizeof(struct lp_data),
84656455Speter};
84756455Speter
848187576SjhbDRIVER_MODULE(plip, ppbus, lp_driver, lp_devclass, lp_module_handler, 0);
849153610SruMODULE_DEPEND(plip, ppbus, 1, 1, 1);
850