xref: /original-bsd/sys/vax/if/if_dmc.c (revision dd262573)
1c3b3a32fSmckusick /*
258c24e32Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
3d3d328d8Sbostic  * All rights reserved.
4c3b3a32fSmckusick  *
5defcbc46Sbostic  * %sccs.include.redist.c%
6d3d328d8Sbostic  *
7*dd262573Sbostic  *	@(#)if_dmc.c	7.10 (Berkeley) 12/16/90
8c3b3a32fSmckusick  */
9bdd6c372Sroot 
10bdd6c372Sroot #include "dmc.h"
11bdd6c372Sroot #if NDMC > 0
126b0400d7Skarels 
13bdd6c372Sroot /*
14bdd6c372Sroot  * DMC11 device driver, internet version
15bdd6c372Sroot  *
166b0400d7Skarels  *	Bill Nesheim
17e8f34b3fStef  *	Cornell University
182c36073aSsam  *
196b0400d7Skarels  *	Lou Salkind
206b0400d7Skarels  *	New York University
21bdd6c372Sroot  */
226b0400d7Skarels 
236b0400d7Skarels /* #define DEBUG	/* for base table dump on fatal error */
246b0400d7Skarels 
25*dd262573Sbostic #include "../include/pte.h"
26bdd6c372Sroot 
27*dd262573Sbostic #include "sys/param.h"
28*dd262573Sbostic #include "sys/systm.h"
29*dd262573Sbostic #include "sys/mbuf.h"
30*dd262573Sbostic #include "sys/buf.h"
31*dd262573Sbostic #include "sys/ioctl.h"		/* must precede tty.h */
32*dd262573Sbostic #include "sys/tty.h"
33*dd262573Sbostic #include "sys/protosw.h"
34*dd262573Sbostic #include "sys/socket.h"
35*dd262573Sbostic #include "sys/syslog.h"
36*dd262573Sbostic #include "sys/vmmac.h"
37*dd262573Sbostic #include "sys/errno.h"
38*dd262573Sbostic #include "sys/time.h"
39*dd262573Sbostic #include "sys/kernel.h"
4002ea2271Sroot 
41*dd262573Sbostic #include "net/if.h"
42*dd262573Sbostic #include "net/netisr.h"
43*dd262573Sbostic #include "net/route.h"
4471555f1fSkarels 
4571555f1fSkarels #ifdef	INET
46*dd262573Sbostic #include "netinet/in.h"
47*dd262573Sbostic #include "netinet/in_systm.h"
48*dd262573Sbostic #include "netinet/in_var.h"
49*dd262573Sbostic #include "netinet/ip.h"
5071555f1fSkarels #endif
5102ea2271Sroot 
52*dd262573Sbostic #include "../include/cpu.h"
53*dd262573Sbostic #include "../include/mtpr.h"
5491194558Sbloom #include "if_uba.h"
5591194558Sbloom #include "if_dmc.h"
56*dd262573Sbostic #include "../uba/ubareg.h"
57*dd262573Sbostic #include "../uba/ubavar.h"
58bdd6c372Sroot 
596b0400d7Skarels 
60d468a3c3Skarels /*
61d468a3c3Skarels  * output timeout value, sec.; should depend on line speed.
62d468a3c3Skarels  */
63d468a3c3Skarels int	dmc_timeout = 20;
646b0400d7Skarels 
65bdd6c372Sroot /*
66bdd6c372Sroot  * Driver information for auto-configuration stuff.
67bdd6c372Sroot  */
68b7ce7f86Ssam int	dmcprobe(), dmcattach(), dmcinit(), dmcioctl();
69d468a3c3Skarels int	dmcoutput(), dmcreset(), dmctimeout();
70bdd6c372Sroot struct	uba_device *dmcinfo[NDMC];
71bdd6c372Sroot u_short	dmcstd[] = { 0 };
72bdd6c372Sroot struct	uba_driver dmcdriver =
73bdd6c372Sroot 	{ dmcprobe, 0, dmcattach, 0, dmcstd, "dmc", dmcinfo };
74bdd6c372Sroot 
75e8f34b3fStef #define NRCV 7
766b0400d7Skarels #define NXMT 3
7771555f1fSkarels #define NCMDS	(NRCV+NXMT+4)	/* size of command queue */
786b0400d7Skarels 
796b0400d7Skarels #define printd if(dmcdebug)printf
806b0400d7Skarels int dmcdebug = 0;
81e8f34b3fStef 
82e8f34b3fStef /* error reporting intervals */
83e8f34b3fStef #define DMC_RPNBFS	50
84e8f34b3fStef #define DMC_RPDSC	1
856b0400d7Skarels #define DMC_RPTMO	10
866b0400d7Skarels #define DMC_RPDCK	10
87e8f34b3fStef 
88e8f34b3fStef struct  dmc_command {
89e8f34b3fStef 	char	qp_cmd;		/* command */
90e8f34b3fStef 	short	qp_ubaddr;	/* buffer address */
91e8f34b3fStef 	short	qp_cc;		/* character count || XMEM */
92e8f34b3fStef 	struct	dmc_command *qp_next;	/* next command on queue */
93e8f34b3fStef };
94e8f34b3fStef 
95e8f34b3fStef struct dmcbufs {
96e8f34b3fStef 	int	ubinfo;		/* from uballoc */
97e8f34b3fStef 	short	cc;		/* buffer size */
98e8f34b3fStef 	short	flags;		/* access control */
99e8f34b3fStef };
100e8f34b3fStef #define	DBUF_OURS	0	/* buffer is available */
101e8f34b3fStef #define	DBUF_DMCS	1	/* buffer claimed by somebody */
102e8f34b3fStef #define	DBUF_XMIT	4	/* transmit buffer */
1036b0400d7Skarels #define	DBUF_RCV	8	/* receive buffer */
104e8f34b3fStef 
105e8f34b3fStef 
106bdd6c372Sroot /*
107bdd6c372Sroot  * DMC software status per interface.
108bdd6c372Sroot  *
109bdd6c372Sroot  * Each interface is referenced by a network interface structure,
110bdd6c372Sroot  * sc_if, which the routing code uses to locate the interface.
111bdd6c372Sroot  * This structure contains the output queue for the interface, its address, ...
112e8f34b3fStef  * We also have, for each interface, a  set of 7 UBA interface structures
113e8f34b3fStef  * for each, which
114e8f34b3fStef  * contain information about the UNIBUS resources held by the interface:
115bdd6c372Sroot  * map registers, buffered data paths, etc.  Information is cached in this
116bdd6c372Sroot  * structure for use by the if_uba.c routines in running the interface
117bdd6c372Sroot  * efficiently.
118bdd6c372Sroot  */
119bdd6c372Sroot struct dmc_softc {
120e8f34b3fStef 	struct	ifnet sc_if;		/* network-visible interface */
1219e791473Skarels 	short	sc_oused;		/* output buffers currently in use */
1229e791473Skarels 	short	sc_iused;		/* input buffers given to DMC */
1239e791473Skarels 	short	sc_flag;		/* flags */
124bdd6c372Sroot 	int	sc_ubinfo;		/* UBA mapping info for base table */
125e8f34b3fStef 	int	sc_errors[4];		/* non-fatal error counters */
126e8f34b3fStef #define sc_datck sc_errors[0]
127e8f34b3fStef #define sc_timeo sc_errors[1]
128e8f34b3fStef #define sc_nobuf sc_errors[2]
129e8f34b3fStef #define sc_disc  sc_errors[3]
130d468a3c3Skarels 	struct	dmcbufs sc_rbufs[NRCV];	/* receive buffer info */
131d468a3c3Skarels 	struct	dmcbufs sc_xbufs[NXMT];	/* transmit buffer info */
132d468a3c3Skarels 	struct	ifubinfo sc_ifuba;	/* UNIBUS resources */
133d468a3c3Skarels 	struct	ifrw sc_ifr[NRCV];	/* UNIBUS receive buffer maps */
134d468a3c3Skarels 	struct	ifxmt sc_ifw[NXMT];	/* UNIBUS receive buffer maps */
135e8f34b3fStef 	/* command queue stuff */
1366b0400d7Skarels 	struct	dmc_command sc_cmdbuf[NCMDS];
137e8f34b3fStef 	struct	dmc_command *sc_qhead;	/* head of command queue */
138e8f34b3fStef 	struct	dmc_command *sc_qtail;	/* tail of command queue */
139e8f34b3fStef 	struct	dmc_command *sc_qactive;	/* command in progress */
140e8f34b3fStef 	struct	dmc_command *sc_qfreeh;	/* head of list of free cmd buffers */
141e8f34b3fStef 	struct	dmc_command *sc_qfreet;	/* tail of list of free cmd buffers */
142e8f34b3fStef 	/* end command queue stuff */
143bdd6c372Sroot } dmc_softc[NDMC];
144bdd6c372Sroot 
145bdd6c372Sroot /* flags */
146d468a3c3Skarels #define DMC_RUNNING	0x01		/* device initialized */
147f72a343aSkarels #define DMC_BMAPPED	0x02		/* base table mapped */
148f72a343aSkarels #define DMC_RESTART	0x04		/* software restart in progress */
149d468a3c3Skarels #define DMC_ONLINE	0x08		/* device running (had a RDYO) */
150bdd6c372Sroot 
151bdd6c372Sroot struct dmc_base {
152bdd6c372Sroot 	short	d_base[128];		/* DMC base table */
153bdd6c372Sroot } dmc_base[NDMC];
154bdd6c372Sroot 
155e8f34b3fStef /* queue manipulation macros */
156e8f34b3fStef #define	QUEUE_AT_HEAD(qp, head, tail) \
157e8f34b3fStef 	(qp)->qp_next = (head); \
158e8f34b3fStef 	(head) = (qp); \
159e8f34b3fStef 	if ((tail) == (struct dmc_command *) 0) \
160e8f34b3fStef 		(tail) = (head)
161e8f34b3fStef 
162e8f34b3fStef #define QUEUE_AT_TAIL(qp, head, tail) \
163e8f34b3fStef 	if ((tail)) \
164e8f34b3fStef 		(tail)->qp_next = (qp); \
165e8f34b3fStef 	else \
166e8f34b3fStef 		(head) = (qp); \
167e8f34b3fStef 	(qp)->qp_next = (struct dmc_command *) 0; \
168e8f34b3fStef 	(tail) = (qp)
169e8f34b3fStef 
170e8f34b3fStef #define DEQUEUE(head, tail) \
171e8f34b3fStef 	(head) = (head)->qp_next;\
172e8f34b3fStef 	if ((head) == (struct dmc_command *) 0)\
173e8f34b3fStef 		(tail) = (head)
174bdd6c372Sroot 
dmcprobe(reg)175bdd6c372Sroot dmcprobe(reg)
176bdd6c372Sroot 	caddr_t reg;
177bdd6c372Sroot {
178bdd6c372Sroot 	register int br, cvec;
179bdd6c372Sroot 	register struct dmcdevice *addr = (struct dmcdevice *)reg;
180bdd6c372Sroot 	register int i;
181bdd6c372Sroot 
182bdd6c372Sroot #ifdef lint
183bdd6c372Sroot 	br = 0; cvec = br; br = cvec;
184bdd6c372Sroot 	dmcrint(0); dmcxint(0);
185bdd6c372Sroot #endif
186bdd6c372Sroot 	addr->bsel1 = DMC_MCLR;
187bdd6c372Sroot 	for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--)
188bdd6c372Sroot 		;
1896b0400d7Skarels 	if ((addr->bsel1 & DMC_RUN) == 0) {
1906b0400d7Skarels 		printf("dmcprobe: can't start device\n" );
191bdd6c372Sroot 		return (0);
1926b0400d7Skarels 	}
193bdd6c372Sroot 	addr->bsel0 = DMC_RQI|DMC_IEI;
1946b0400d7Skarels 	/* let's be paranoid */
1956b0400d7Skarels 	addr->bsel0 |= DMC_RQI|DMC_IEI;
1966b0400d7Skarels 	DELAY(1000000);
197bdd6c372Sroot 	addr->bsel1 = DMC_MCLR;
198bdd6c372Sroot 	for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--)
199bdd6c372Sroot 		;
200bdd6c372Sroot 	return (1);
201bdd6c372Sroot }
202bdd6c372Sroot 
203bdd6c372Sroot /*
204bdd6c372Sroot  * Interface exists: make available by filling in network interface
205bdd6c372Sroot  * record.  System will initialize the interface when it is ready
206bdd6c372Sroot  * to accept packets.
207bdd6c372Sroot  */
dmcattach(ui)208bdd6c372Sroot dmcattach(ui)
209bdd6c372Sroot 	register struct uba_device *ui;
210bdd6c372Sroot {
211bdd6c372Sroot 	register struct dmc_softc *sc = &dmc_softc[ui->ui_unit];
212bdd6c372Sroot 
213bdd6c372Sroot 	sc->sc_if.if_unit = ui->ui_unit;
214bdd6c372Sroot 	sc->sc_if.if_name = "dmc";
215bdd6c372Sroot 	sc->sc_if.if_mtu = DMCMTU;
216bdd6c372Sroot 	sc->sc_if.if_init = dmcinit;
217bdd6c372Sroot 	sc->sc_if.if_output = dmcoutput;
218b7ce7f86Ssam 	sc->sc_if.if_ioctl = dmcioctl;
219222ab732Sroot 	sc->sc_if.if_reset = dmcreset;
220d468a3c3Skarels 	sc->sc_if.if_watchdog = dmctimeout;
221e8f34b3fStef 	sc->sc_if.if_flags = IFF_POINTOPOINT;
22271555f1fSkarels 	sc->sc_ifuba.iff_flags = UBA_CANTWAIT;
223e8f34b3fStef 
22471555f1fSkarels 	if_attach(&sc->sc_if);
225bdd6c372Sroot }
226bdd6c372Sroot 
227bdd6c372Sroot /*
228bdd6c372Sroot  * Reset of interface after UNIBUS reset.
229a08469b0Skarels  * If interface is on specified UBA, reset its state.
230bdd6c372Sroot  */
dmcreset(unit,uban)231bdd6c372Sroot dmcreset(unit, uban)
232bdd6c372Sroot 	int unit, uban;
233bdd6c372Sroot {
234bdd6c372Sroot 	register struct uba_device *ui;
235e8f34b3fStef 	register struct dmc_softc *sc = &dmc_softc[unit];
236bdd6c372Sroot 
237bdd6c372Sroot 	if (unit >= NDMC || (ui = dmcinfo[unit]) == 0 || ui->ui_alive == 0 ||
238bdd6c372Sroot 	    ui->ui_ubanum != uban)
239bdd6c372Sroot 		return;
240bdd6c372Sroot 	printf(" dmc%d", unit);
2416b0400d7Skarels 	sc->sc_flag = 0;
242a08469b0Skarels 	sc->sc_if.if_flags &= ~IFF_RUNNING;
243bdd6c372Sroot 	dmcinit(unit);
244bdd6c372Sroot }
245bdd6c372Sroot 
246bdd6c372Sroot /*
247bdd6c372Sroot  * Initialization of interface; reinitialize UNIBUS usage.
248bdd6c372Sroot  */
dmcinit(unit)249bdd6c372Sroot dmcinit(unit)
250bdd6c372Sroot 	int unit;
251bdd6c372Sroot {
252bdd6c372Sroot 	register struct dmc_softc *sc = &dmc_softc[unit];
253bdd6c372Sroot 	register struct uba_device *ui = dmcinfo[unit];
254bdd6c372Sroot 	register struct dmcdevice *addr;
255b7ce7f86Ssam 	register struct ifnet *ifp = &sc->sc_if;
256e8f34b3fStef 	register struct ifrw *ifrw;
257e8f34b3fStef 	register struct ifxmt *ifxp;
258e8f34b3fStef 	register struct dmcbufs *rp;
2596b0400d7Skarels 	register struct dmc_command *qp;
260a08469b0Skarels 	struct ifaddr *ifa;
261bdd6c372Sroot 	int base;
2626b0400d7Skarels 	int s;
263bdd6c372Sroot 
264e8f34b3fStef 	addr = (struct dmcdevice *)ui->ui_addr;
265e8f34b3fStef 
266a08469b0Skarels 	/*
267a08469b0Skarels 	 * Check to see that an address has been set
268a08469b0Skarels 	 * (both local and destination for an address family).
269a08469b0Skarels 	 */
270a08469b0Skarels 	for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
271fbf380ddSkarels 		if (ifa->ifa_addr->sa_family && ifa->ifa_dstaddr->sa_family)
272a08469b0Skarels 			break;
273a08469b0Skarels 	if (ifa == (struct ifaddr *) 0)
274e8f34b3fStef 		return;
275e8f34b3fStef 
276e8f34b3fStef 	if ((addr->bsel1&DMC_RUN) == 0) {
277e8f34b3fStef 		printf("dmcinit: DMC not running\n");
278a08469b0Skarels 		ifp->if_flags &= ~IFF_UP;
279e8f34b3fStef 		return;
280bdd6c372Sroot 	}
281e8f34b3fStef 	/* map base table */
282e8f34b3fStef 	if ((sc->sc_flag & DMC_BMAPPED) == 0) {
283e8f34b3fStef 		sc->sc_ubinfo = uballoc(ui->ui_ubanum,
284e8f34b3fStef 			(caddr_t)&dmc_base[unit], sizeof (struct dmc_base), 0);
285e8f34b3fStef 		sc->sc_flag |= DMC_BMAPPED;
286e8f34b3fStef 	}
287e8f34b3fStef 	/* initialize UNIBUS resources */
288e8f34b3fStef 	sc->sc_iused = sc->sc_oused = 0;
289a08469b0Skarels 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
29071555f1fSkarels 		if (if_ubaminit(&sc->sc_ifuba, ui->ui_ubanum,
29171555f1fSkarels 		    sizeof(struct dmc_header), (int)btoc(DMCMTU),
29271555f1fSkarels 		    sc->sc_ifr, NRCV, sc->sc_ifw, NXMT) == 0) {
293a08469b0Skarels 			printf("dmc%d: can't allocate uba resources\n", unit);
294b7ce7f86Ssam 			ifp->if_flags &= ~IFF_UP;
295bdd6c372Sroot 			return;
296bdd6c372Sroot 		}
297a08469b0Skarels 		ifp->if_flags |= IFF_RUNNING;
298e8f34b3fStef 	}
299d468a3c3Skarels 	sc->sc_flag &= ~DMC_ONLINE;
300f72a343aSkarels 	sc->sc_flag |= DMC_RUNNING;
301d468a3c3Skarels 	/*
302d468a3c3Skarels 	 * Limit packets enqueued until we see if we're on the air.
303d468a3c3Skarels 	 */
304d468a3c3Skarels 	ifp->if_snd.ifq_maxlen = 3;
305e8f34b3fStef 
306e8f34b3fStef 	/* initialize buffer pool */
3076b0400d7Skarels 	/* receives */
30871555f1fSkarels 	ifrw = &sc->sc_ifr[0];
309e8f34b3fStef 	for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
310e578e9bdSkarels 		rp->ubinfo = UBAI_ADDR(ifrw->ifrw_info);
3116b0400d7Skarels 		rp->cc = DMCMTU + sizeof (struct dmc_header);
312e8f34b3fStef 		rp->flags = DBUF_OURS|DBUF_RCV;
313e8f34b3fStef 		ifrw++;
314e8f34b3fStef 	}
315e8f34b3fStef 	/* transmits */
31671555f1fSkarels 	ifxp = &sc->sc_ifw[0];
317e8f34b3fStef 	for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
318e578e9bdSkarels 		rp->ubinfo = UBAI_ADDR(ifxp->ifw_info);
319e8f34b3fStef 		rp->cc = 0;
320e8f34b3fStef 		rp->flags = DBUF_OURS|DBUF_XMIT;
321e8f34b3fStef 		ifxp++;
322e8f34b3fStef 	}
3236b0400d7Skarels 
3246b0400d7Skarels 	/* set up command queues */
3256b0400d7Skarels 	sc->sc_qfreeh = sc->sc_qfreet
3266b0400d7Skarels 		 = sc->sc_qhead = sc->sc_qtail = sc->sc_qactive =
3276b0400d7Skarels 		(struct dmc_command *)0;
3286b0400d7Skarels 	/* set up free command buffer list */
3296b0400d7Skarels 	for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) {
3306b0400d7Skarels 		QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
3316b0400d7Skarels 	}
3326b0400d7Skarels 
333e8f34b3fStef 	/* base in */
334e578e9bdSkarels 	base = UBAI_ADDR(sc->sc_ubinfo);
335e578e9bdSkarels 	dmcload(sc, DMC_BASEI, (u_short)base, (base>>2) & DMC_XMEM);
336e8f34b3fStef 	/* specify half duplex operation, flags tell if primary */
337e8f34b3fStef 	/* or secondary station */
338e8f34b3fStef 	if (ui->ui_flags == 0)
339f72a343aSkarels 		/* use DDCMP mode in full duplex */
340e8f34b3fStef 		dmcload(sc, DMC_CNTLI, 0, 0);
341e8f34b3fStef 	else if (ui->ui_flags == 1)
342e8f34b3fStef 		/* use MAINTENENCE mode */
343e8f34b3fStef 		dmcload(sc, DMC_CNTLI, 0, DMC_MAINT );
344e8f34b3fStef 	else if (ui->ui_flags == 2)
345e8f34b3fStef 		/* use DDCMP half duplex as primary station */
346e8f34b3fStef 		dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX);
347e8f34b3fStef 	else if (ui->ui_flags == 3)
348e8f34b3fStef 		/* use DDCMP half duplex as secondary station */
349e8f34b3fStef 		dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX | DMC_SEC);
350e8f34b3fStef 
3516b0400d7Skarels 	/* enable operation done interrupts */
3526b0400d7Skarels 	while ((addr->bsel2 & DMC_IEO) == 0)
3536b0400d7Skarels 		addr->bsel2 |= DMC_IEO;
3546b0400d7Skarels 	s = spl5();
355e8f34b3fStef 	/* queue first NRCV buffers for DMC to fill */
356e8f34b3fStef 	for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
357e8f34b3fStef 		rp->flags |= DBUF_DMCS;
358e8f34b3fStef 		dmcload(sc, DMC_READ, rp->ubinfo,
359e8f34b3fStef 			(((rp->ubinfo>>2)&DMC_XMEM) | rp->cc));
360e8f34b3fStef 		sc->sc_iused++;
361e8f34b3fStef 	}
3626b0400d7Skarels 	splx(s);
363b7ce7f86Ssam }
364bdd6c372Sroot 
365bdd6c372Sroot /*
366bdd6c372Sroot  * Start output on interface.  Get another datagram
367bdd6c372Sroot  * to send from the interface queue and map it to
368bdd6c372Sroot  * the interface before starting output.
369e8f34b3fStef  *
370e8f34b3fStef  * Must be called at spl 5
371bdd6c372Sroot  */
dmcstart(unit)372d468a3c3Skarels dmcstart(unit)
373d468a3c3Skarels 	int unit;
374bdd6c372Sroot {
375bdd6c372Sroot 	register struct dmc_softc *sc = &dmc_softc[unit];
376bdd6c372Sroot 	struct mbuf *m;
377e8f34b3fStef 	register struct dmcbufs *rp;
378e8f34b3fStef 	register int n;
379bdd6c372Sroot 
380bdd6c372Sroot 	/*
381e8f34b3fStef 	 * Dequeue up to NXMT requests and map them to the UNIBUS.
382e8f34b3fStef 	 * If no more requests, or no dmc buffers available, just return.
383bdd6c372Sroot 	 */
384e8f34b3fStef 	n = 0;
385e8f34b3fStef 	for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) {
386e8f34b3fStef 		/* find an available buffer */
387e8f34b3fStef 		if ((rp->flags & DBUF_DMCS) == 0) {
388bdd6c372Sroot 			IF_DEQUEUE(&sc->sc_if.if_snd, m);
389bdd6c372Sroot 			if (m == 0)
390bdd6c372Sroot 				return;
391e8f34b3fStef 			/* mark it dmcs */
392e8f34b3fStef 			rp->flags |= (DBUF_DMCS);
393bdd6c372Sroot 			/*
394e8f34b3fStef 			 * Have request mapped to UNIBUS for transmission
395e8f34b3fStef 			 * and start the output.
396bdd6c372Sroot 			 */
39771555f1fSkarels 			rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m);
3986b0400d7Skarels 			rp->cc &= DMC_CCOUNT;
399d468a3c3Skarels 			if (++sc->sc_oused == 1)
400d468a3c3Skarels 				sc->sc_if.if_timer = dmc_timeout;
401e8f34b3fStef 			dmcload(sc, DMC_WRITE, rp->ubinfo,
402e8f34b3fStef 				rp->cc | ((rp->ubinfo>>2)&DMC_XMEM));
403e8f34b3fStef 		}
404e8f34b3fStef 		n++;
405e8f34b3fStef 	}
406bdd6c372Sroot }
407bdd6c372Sroot 
408bdd6c372Sroot /*
409bdd6c372Sroot  * Utility routine to load the DMC device registers.
410bdd6c372Sroot  */
dmcload(sc,type,w0,w1)411bdd6c372Sroot dmcload(sc, type, w0, w1)
412bdd6c372Sroot 	register struct dmc_softc *sc;
413e578e9bdSkarels 	int type;
414e578e9bdSkarels 	u_short w0, w1;
415bdd6c372Sroot {
416bdd6c372Sroot 	register struct dmcdevice *addr;
417e8f34b3fStef 	register int unit, sps;
418e8f34b3fStef 	register struct dmc_command *qp;
419bdd6c372Sroot 
4206b0400d7Skarels 	unit = sc - dmc_softc;
421bdd6c372Sroot 	addr = (struct dmcdevice *)dmcinfo[unit]->ui_addr;
422bdd6c372Sroot 	sps = spl5();
423e8f34b3fStef 
424e8f34b3fStef 	/* grab a command buffer from the free list */
425e8f34b3fStef 	if ((qp = sc->sc_qfreeh) == (struct dmc_command *)0)
426e8f34b3fStef 		panic("dmc command queue overflow");
427e8f34b3fStef 	DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet);
428e8f34b3fStef 
429e8f34b3fStef 	/* fill in requested info */
430e8f34b3fStef 	qp->qp_cmd = (type | DMC_RQI);
431e8f34b3fStef 	qp->qp_ubaddr = w0;
432e8f34b3fStef 	qp->qp_cc = w1;
433e8f34b3fStef 
434e8f34b3fStef 	if (sc->sc_qactive) {	/* command in progress */
435e8f34b3fStef 		if (type == DMC_READ) {
436e8f34b3fStef 			QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail);
437e8f34b3fStef 		} else {
438e8f34b3fStef 			QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail);
439e8f34b3fStef 		}
440e8f34b3fStef 	} else {	/* command port free */
441e8f34b3fStef 		sc->sc_qactive = qp;
442e8f34b3fStef 		addr->bsel0 = qp->qp_cmd;
443bdd6c372Sroot 		dmcrint(unit);
444e8f34b3fStef 	}
445bdd6c372Sroot 	splx(sps);
446bdd6c372Sroot }
447bdd6c372Sroot 
448bdd6c372Sroot /*
449bdd6c372Sroot  * DMC interface receiver interrupt.
450bdd6c372Sroot  * Ready to accept another command,
451bdd6c372Sroot  * pull one off the command queue.
452bdd6c372Sroot  */
dmcrint(unit)453bdd6c372Sroot dmcrint(unit)
454bdd6c372Sroot 	int unit;
455bdd6c372Sroot {
456bdd6c372Sroot 	register struct dmc_softc *sc;
457bdd6c372Sroot 	register struct dmcdevice *addr;
458e8f34b3fStef 	register struct dmc_command *qp;
459bdd6c372Sroot 	register int n;
460bdd6c372Sroot 
461bdd6c372Sroot 	addr = (struct dmcdevice *)dmcinfo[unit]->ui_addr;
462bdd6c372Sroot 	sc = &dmc_softc[unit];
463e8f34b3fStef 	if ((qp = sc->sc_qactive) == (struct dmc_command *) 0) {
4646b0400d7Skarels 		printf("dmc%d: dmcrint no command\n", unit);
465e8f34b3fStef 		return;
466e8f34b3fStef 	}
467bdd6c372Sroot 	while (addr->bsel0&DMC_RDYI) {
468e8f34b3fStef 		addr->sel4 = qp->qp_ubaddr;
469e8f34b3fStef 		addr->sel6 = qp->qp_cc;
470bdd6c372Sroot 		addr->bsel0 &= ~(DMC_IEI|DMC_RQI);
471e8f34b3fStef 		/* free command buffer */
472e8f34b3fStef 		QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
473e8f34b3fStef 		while (addr->bsel0 & DMC_RDYI) {
474e8f34b3fStef 			/*
475e8f34b3fStef 			 * Can't check for RDYO here 'cause
476e8f34b3fStef 			 * this routine isn't reentrant!
477e8f34b3fStef 			 */
478e8f34b3fStef 			DELAY(5);
479e8f34b3fStef 		}
480e8f34b3fStef 		/* move on to next command */
481e8f34b3fStef 		if ((sc->sc_qactive = sc->sc_qhead) == (struct dmc_command *)0)
4826b0400d7Skarels 			break;		/* all done */
483e8f34b3fStef 		/* more commands to do, start the next one */
484e8f34b3fStef 		qp = sc->sc_qactive;
485e8f34b3fStef 		DEQUEUE(sc->sc_qhead, sc->sc_qtail);
486e8f34b3fStef 		addr->bsel0 = qp->qp_cmd;
487bdd6c372Sroot 		n = RDYSCAN;
4886b0400d7Skarels 		while (n-- > 0)
4896b0400d7Skarels 			if ((addr->bsel0&DMC_RDYI) || (addr->bsel2&DMC_RDYO))
4906b0400d7Skarels 				break;
491bdd6c372Sroot 	}
492e8f34b3fStef 	if (sc->sc_qactive) {
493e8f34b3fStef 		addr->bsel0 |= DMC_IEI|DMC_RQI;
494e8f34b3fStef 		/* VMS does it twice !*$%@# */
495e8f34b3fStef 		addr->bsel0 |= DMC_IEI|DMC_RQI;
496e8f34b3fStef 	}
4976b0400d7Skarels 
498bdd6c372Sroot }
499bdd6c372Sroot 
500bdd6c372Sroot /*
501bdd6c372Sroot  * DMC interface transmitter interrupt.
502e8f34b3fStef  * A transfer may have completed, check for errors.
503bdd6c372Sroot  * If it was a read, notify appropriate protocol.
504bdd6c372Sroot  * If it was a write, pull the next one off the queue.
505bdd6c372Sroot  */
dmcxint(unit)506bdd6c372Sroot dmcxint(unit)
507bdd6c372Sroot 	int unit;
508bdd6c372Sroot {
509bdd6c372Sroot 	register struct dmc_softc *sc;
5104ed66730Ssam 	register struct ifnet *ifp;
511bdd6c372Sroot 	struct uba_device *ui = dmcinfo[unit];
512bdd6c372Sroot 	struct dmcdevice *addr;
513bdd6c372Sroot 	struct mbuf *m;
5146b0400d7Skarels 	struct ifqueue *inq;
515f72a343aSkarels 	int arg, pkaddr, cmd, len, s;
516e8f34b3fStef 	register struct ifrw *ifrw;
517e8f34b3fStef 	register struct dmcbufs *rp;
5186b0400d7Skarels 	register struct ifxmt *ifxp;
5196b0400d7Skarels 	struct dmc_header *dh;
5206b0400d7Skarels 	int off, resid;
521bdd6c372Sroot 
522bdd6c372Sroot 	addr = (struct dmcdevice *)ui->ui_addr;
523bdd6c372Sroot 	sc = &dmc_softc[unit];
5244ed66730Ssam 	ifp = &sc->sc_if;
525e8f34b3fStef 
5266b0400d7Skarels 	while (addr->bsel2 & DMC_RDYO) {
5276b0400d7Skarels 
528e8f34b3fStef 		cmd = addr->bsel2 & 0xff;
529e8f34b3fStef 		arg = addr->sel6 & 0xffff;
530e8f34b3fStef 		/* reconstruct UNIBUS address of buffer returned to us */
531e8f34b3fStef 		pkaddr = ((arg&DMC_XMEM)<<2) | (addr->sel4 & 0xffff);
532e8f34b3fStef 		/* release port */
533e8f34b3fStef 		addr->bsel2 &= ~DMC_RDYO;
534fe17efc2Ssam 		switch (cmd & 07) {
535bdd6c372Sroot 
536bdd6c372Sroot 		case DMC_OUR:
537bdd6c372Sroot 			/*
538e8f34b3fStef 			 * A read has completed.
539e8f34b3fStef 			 * Pass packet to type specific
540bdd6c372Sroot 			 * higher-level input routine.
541bdd6c372Sroot 			 */
5424ed66730Ssam 			ifp->if_ipackets++;
543e8f34b3fStef 			/* find location in dmcuba struct */
54471555f1fSkarels 			ifrw= &sc->sc_ifr[0];
5456b0400d7Skarels 			for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
546e8f34b3fStef 				if(rp->ubinfo == pkaddr)
5476b0400d7Skarels 					break;
548e8f34b3fStef 				ifrw++;
549e8f34b3fStef 			}
5506b0400d7Skarels 			if (rp >= &sc->sc_rbufs[NRCV])
5516b0400d7Skarels 				panic("dmc rcv");
5526b0400d7Skarels 			if ((rp->flags & DBUF_DMCS) == 0)
5536b0400d7Skarels 				printf("dmc%d: done unalloc rbuf\n", unit);
5546b0400d7Skarels 
5556b0400d7Skarels 			len = (arg & DMC_CCOUNT) - sizeof (struct dmc_header);
5566b0400d7Skarels 			if (len < 0 || len > DMCMTU) {
5576b0400d7Skarels 				ifp->if_ierrors++;
5586b0400d7Skarels 				printd("dmc%d: bad rcv pkt addr 0x%x len 0x%x\n",
5596b0400d7Skarels 				    unit, pkaddr, len);
5606b0400d7Skarels 				goto setup;
5616b0400d7Skarels 			}
5626b0400d7Skarels 			/*
5636b0400d7Skarels 			 * Deal with trailer protocol: if type is trailer
5646b0400d7Skarels 			 * get true type from first 16-bit word past data.
5656b0400d7Skarels 			 * Remember that type was trailer by setting off.
5666b0400d7Skarels 			 */
5676b0400d7Skarels 			dh = (struct dmc_header *)ifrw->ifrw_addr;
5686b0400d7Skarels 			dh->dmc_type = ntohs((u_short)dh->dmc_type);
5696b0400d7Skarels #define dmcdataaddr(dh, off, type)	((type)(((caddr_t)((dh)+1)+(off))))
5706b0400d7Skarels 			if (dh->dmc_type >= DMC_TRAILER &&
5716b0400d7Skarels 			    dh->dmc_type < DMC_TRAILER+DMC_NTRAILER) {
5726b0400d7Skarels 				off = (dh->dmc_type - DMC_TRAILER) * 512;
5736b0400d7Skarels 				if (off >= DMCMTU)
5746b0400d7Skarels 					goto setup;		/* sanity */
5756b0400d7Skarels 				dh->dmc_type = ntohs(*dmcdataaddr(dh, off, u_short *));
5766b0400d7Skarels 				resid = ntohs(*(dmcdataaddr(dh, off+2, u_short *)));
5776b0400d7Skarels 				if (off + resid > len)
5786b0400d7Skarels 					goto setup;		/* sanity */
5796b0400d7Skarels 				len = off + resid;
5806b0400d7Skarels 			} else
5816b0400d7Skarels 				off = 0;
5826b0400d7Skarels 			if (len == 0)
583e8f34b3fStef 				goto setup;
584e8f34b3fStef 
5856b0400d7Skarels 			/*
5866b0400d7Skarels 			 * Pull packet off interface.  Off is nonzero if
5876b0400d7Skarels 			 * packet has trailing header; dmc_get will then
5886b0400d7Skarels 			 * force this header information to be at the front,
5896b0400d7Skarels 			 * but we still have to drop the type and length
5906b0400d7Skarels 			 * which are at the front of any trailer data.
5916b0400d7Skarels 			 */
59271555f1fSkarels 			m = if_ubaget(&sc->sc_ifuba, ifrw, len, off, ifp);
5936b0400d7Skarels 			if (m == 0)
5946b0400d7Skarels 				goto setup;
5956b0400d7Skarels 			switch (dh->dmc_type) {
5966b0400d7Skarels 
597bdd6c372Sroot #ifdef INET
5986b0400d7Skarels 			case DMC_IPTYPE:
59920bd5ff9Swnj 				schednetisr(NETISR_IP);
600bdd6c372Sroot 				inq = &ipintrq;
601bdd6c372Sroot 				break;
602bdd6c372Sroot #endif
603bdd6c372Sroot 			default:
6046b0400d7Skarels 				m_freem(m);
605bdd6c372Sroot 				goto setup;
606bdd6c372Sroot 			}
607e8f34b3fStef 
608f72a343aSkarels 			s = splimp();
609fa57dce4Swnj 			if (IF_QFULL(inq)) {
610fa57dce4Swnj 				IF_DROP(inq);
611871045d0Ssam 				m_freem(m);
612fa57dce4Swnj 			} else
613bdd6c372Sroot 				IF_ENQUEUE(inq, m);
614f72a343aSkarels 			splx(s);
6156b0400d7Skarels 
616bdd6c372Sroot 	setup:
6176b0400d7Skarels 			/* is this needed? */
618e578e9bdSkarels 			rp->ubinfo = UBAI_ADDR(ifrw->ifrw_info);
6196b0400d7Skarels 
6206b0400d7Skarels 			dmcload(sc, DMC_READ, rp->ubinfo,
6216b0400d7Skarels 			    ((rp->ubinfo >> 2) & DMC_XMEM) | rp->cc);
622e8f34b3fStef 			break;
623bdd6c372Sroot 
624bdd6c372Sroot 		case DMC_OUX:
625bdd6c372Sroot 			/*
626bdd6c372Sroot 			 * A write has completed, start another
627bdd6c372Sroot 			 * transfer if there is more data to send.
628bdd6c372Sroot 			 */
6294ed66730Ssam 			ifp->if_opackets++;
630e8f34b3fStef 			/* find associated dmcbuf structure */
63171555f1fSkarels 			ifxp = &sc->sc_ifw[0];
6326b0400d7Skarels 			for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
633e8f34b3fStef 				if(rp->ubinfo == pkaddr)
6346b0400d7Skarels 					break;
6356b0400d7Skarels 				ifxp++;
636bdd6c372Sroot 			}
6376b0400d7Skarels 			if (rp >= &sc->sc_xbufs[NXMT]) {
638e8f34b3fStef 				printf("dmc%d: bad packet address 0x%x\n",
639e8f34b3fStef 				    unit, pkaddr);
640e8f34b3fStef 				break;
6416b0400d7Skarels 			}
642e8f34b3fStef 			if ((rp->flags & DBUF_DMCS) == 0)
6436b0400d7Skarels 				printf("dmc%d: unallocated packet 0x%x\n",
6446b0400d7Skarels 				    unit, pkaddr);
645e8f34b3fStef 			/* mark buffer free */
64671555f1fSkarels 			if (ifxp->ifw_xtofree) {
64771555f1fSkarels 				(void)m_freem(ifxp->ifw_xtofree);
64871555f1fSkarels 				ifxp->ifw_xtofree = 0;
6496b0400d7Skarels 			}
6506b0400d7Skarels 			rp->flags &= ~DBUF_DMCS;
651d468a3c3Skarels 			if (--sc->sc_oused == 0)
652d468a3c3Skarels 				sc->sc_if.if_timer = 0;
653d468a3c3Skarels 			else
654d468a3c3Skarels 				sc->sc_if.if_timer = dmc_timeout;
655d468a3c3Skarels 			if ((sc->sc_flag & DMC_ONLINE) == 0) {
656d468a3c3Skarels 				extern int ifqmaxlen;
657d468a3c3Skarels 
658d468a3c3Skarels 				/*
659d468a3c3Skarels 				 * We're on the air.
660d468a3c3Skarels 				 * Open the queue to the usual value.
661d468a3c3Skarels 				 */
662d468a3c3Skarels 				sc->sc_flag |= DMC_ONLINE;
663d468a3c3Skarels 				ifp->if_snd.ifq_maxlen = ifqmaxlen;
664d468a3c3Skarels 			}
665e8f34b3fStef 			break;
666bdd6c372Sroot 
667bdd6c372Sroot 		case DMC_CNTLO:
668bdd6c372Sroot 			arg &= DMC_CNTMASK;
669bdd6c372Sroot 			if (arg & DMC_FATAL) {
670d468a3c3Skarels 				if (arg != DMC_START)
671d468a3c3Skarels 					log(LOG_ERR,
672d468a3c3Skarels 					    "dmc%d: fatal error, flags=%b\n",
673e8f34b3fStef 					    unit, arg, CNTLO_BITS);
6746b0400d7Skarels 				dmcrestart(unit);
675e8f34b3fStef 				break;
6766b0400d7Skarels 			}
677bdd6c372Sroot 			/* ACCUMULATE STATISTICS */
678e8f34b3fStef 			switch(arg) {
679e8f34b3fStef 			case DMC_NOBUFS:
680e8f34b3fStef 				ifp->if_ierrors++;
6816b0400d7Skarels 				if ((sc->sc_nobuf++ % DMC_RPNBFS) == 0)
682e8f34b3fStef 					goto report;
6836b0400d7Skarels 				break;
684e8f34b3fStef 			case DMC_DISCONN:
6856b0400d7Skarels 				if ((sc->sc_disc++ % DMC_RPDSC) == 0)
686e8f34b3fStef 					goto report;
6876b0400d7Skarels 				break;
688e8f34b3fStef 			case DMC_TIMEOUT:
6896b0400d7Skarels 				if ((sc->sc_timeo++ % DMC_RPTMO) == 0)
690e8f34b3fStef 					goto report;
6916b0400d7Skarels 				break;
692e8f34b3fStef 			case DMC_DATACK:
693e8f34b3fStef 				ifp->if_oerrors++;
6946b0400d7Skarels 				if ((sc->sc_datck++ % DMC_RPDCK) == 0)
695e8f34b3fStef 					goto report;
6966b0400d7Skarels 				break;
697e8f34b3fStef 			default:
698e8f34b3fStef 				goto report;
699bdd6c372Sroot 			}
700e8f34b3fStef 			break;
701e8f34b3fStef 		report:
7026b0400d7Skarels 			printd("dmc%d: soft error, flags=%b\n", unit,
7036b0400d7Skarels 			    arg, CNTLO_BITS);
7046b0400d7Skarels 			if ((sc->sc_flag & DMC_RESTART) == 0) {
7056b0400d7Skarels 				/*
7066b0400d7Skarels 				 * kill off the dmc to get things
7076b0400d7Skarels 				 * going again by generating a
7086b0400d7Skarels 				 * procedure error
7096b0400d7Skarels 				 */
7106b0400d7Skarels 				sc->sc_flag |= DMC_RESTART;
711e578e9bdSkarels 				arg = UBAI_ADDR(sc->sc_ubinfo);
7126b0400d7Skarels 				dmcload(sc, DMC_BASEI, arg, (arg>>2)&DMC_XMEM);
713e8f34b3fStef 			}
714e8f34b3fStef 			break;
715bdd6c372Sroot 
716bdd6c372Sroot 		default:
717bdd6c372Sroot 			printf("dmc%d: bad control %o\n", unit, cmd);
7186b0400d7Skarels 			break;
719bdd6c372Sroot 		}
7206b0400d7Skarels 	}
7216b0400d7Skarels 	dmcstart(unit);
722e8f34b3fStef 	return;
723bdd6c372Sroot }
724bdd6c372Sroot 
725bdd6c372Sroot /*
726bdd6c372Sroot  * DMC output routine.
7276b0400d7Skarels  * Encapsulate a packet of type family for the dmc.
7286b0400d7Skarels  * Use trailer local net encapsulation if enough data in first
7296b0400d7Skarels  * packet leaves a multiple of 512 bytes of data in remainder.
730bdd6c372Sroot  */
dmcoutput(ifp,m0,dst)7316b0400d7Skarels dmcoutput(ifp, m0, dst)
732bdd6c372Sroot 	register struct ifnet *ifp;
7336b0400d7Skarels 	register struct mbuf *m0;
7348d171e64Ssam 	struct sockaddr *dst;
735bdd6c372Sroot {
7366b0400d7Skarels 	int type, error, s;
7376b0400d7Skarels 	register struct mbuf *m = m0;
7386b0400d7Skarels 	register struct dmc_header *dh;
7396b0400d7Skarels 	register int off;
740bdd6c372Sroot 
741d468a3c3Skarels 	if ((ifp->if_flags & IFF_UP) == 0) {
742d468a3c3Skarels 		error = ENETDOWN;
743d468a3c3Skarels 		goto bad;
744d468a3c3Skarels 	}
745d468a3c3Skarels 
7466b0400d7Skarels 	switch (dst->sa_family) {
7476b0400d7Skarels #ifdef	INET
7486b0400d7Skarels 	case AF_INET:
749fbf380ddSkarels 		off = m->m_pkthdr.len - m->m_len;
7506b0400d7Skarels 		if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
7516b0400d7Skarels 		if (off > 0 && (off & 0x1ff) == 0 &&
752fbf380ddSkarels 		    (m->m_flags & M_EXT) == 0 &&
753fbf380ddSkarels 		    m->m_data >= m->m_pktdat + 2 * sizeof (u_short)) {
7546b0400d7Skarels 			type = DMC_TRAILER + (off>>9);
755fbf380ddSkarels 			m->m_data -= 2 * sizeof (u_short);
7566b0400d7Skarels 			m->m_len += 2 * sizeof (u_short);
7576b0400d7Skarels 			*mtod(m, u_short *) = htons((u_short)DMC_IPTYPE);
7586b0400d7Skarels 			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
7596b0400d7Skarels 			goto gottrailertype;
760bdd6c372Sroot 		}
7616b0400d7Skarels 		type = DMC_IPTYPE;
7626b0400d7Skarels 		off = 0;
7636b0400d7Skarels 		goto gottype;
7646b0400d7Skarels #endif
7656b0400d7Skarels 
7666b0400d7Skarels 	case AF_UNSPEC:
7676b0400d7Skarels 		dh = (struct dmc_header *)dst->sa_data;
7686b0400d7Skarels 		type = dh->dmc_type;
7696b0400d7Skarels 		goto gottype;
7706b0400d7Skarels 
7716b0400d7Skarels 	default:
7726b0400d7Skarels 		printf("dmc%d: can't handle af%d\n", ifp->if_unit,
7736b0400d7Skarels 			dst->sa_family);
7746b0400d7Skarels 		error = EAFNOSUPPORT;
7756b0400d7Skarels 		goto bad;
7766b0400d7Skarels 	}
7776b0400d7Skarels 
7786b0400d7Skarels gottrailertype:
7796b0400d7Skarels 	/*
7806b0400d7Skarels 	 * Packet to be sent as a trailer; move first packet
7816b0400d7Skarels 	 * (control information) to end of chain.
7826b0400d7Skarels 	 */
7836b0400d7Skarels 	while (m->m_next)
7846b0400d7Skarels 		m = m->m_next;
7856b0400d7Skarels 	m->m_next = m0;
7866b0400d7Skarels 	m = m0->m_next;
7876b0400d7Skarels 	m0->m_next = 0;
7886b0400d7Skarels 	m0 = m;
7896b0400d7Skarels 
7906b0400d7Skarels gottype:
7916b0400d7Skarels 	/*
7926b0400d7Skarels 	 * Add local network header
7936b0400d7Skarels 	 * (there is space for a uba on a vax to step on)
7946b0400d7Skarels 	 */
795fbf380ddSkarels 	M_PREPEND(m, sizeof(struct dmc_header), M_DONTWAIT);
7966b0400d7Skarels 	if (m == 0) {
7976b0400d7Skarels 		error = ENOBUFS;
7986b0400d7Skarels 		goto bad;
7996b0400d7Skarels 	}
8006b0400d7Skarels 	dh = mtod(m, struct dmc_header *);
8016b0400d7Skarels 	dh->dmc_type = htons((u_short)type);
8026b0400d7Skarels 
8036b0400d7Skarels 	/*
8046b0400d7Skarels 	 * Queue message on interface, and start output if interface
8056b0400d7Skarels 	 * not yet active.
8066b0400d7Skarels 	 */
8076b0400d7Skarels 	s = splimp();
808fa57dce4Swnj 	if (IF_QFULL(&ifp->if_snd)) {
809fa57dce4Swnj 		IF_DROP(&ifp->if_snd);
8108d171e64Ssam 		m_freem(m);
811fa57dce4Swnj 		splx(s);
8121c805374Ssam 		return (ENOBUFS);
813fa57dce4Swnj 	}
814bdd6c372Sroot 	IF_ENQUEUE(&ifp->if_snd, m);
815bdd6c372Sroot 	dmcstart(ifp->if_unit);
816bdd6c372Sroot 	splx(s);
8171c805374Ssam 	return (0);
8186b0400d7Skarels 
8196b0400d7Skarels bad:
8206b0400d7Skarels 	m_freem(m0);
8216b0400d7Skarels 	return (error);
822bdd6c372Sroot }
823b7ce7f86Ssam 
8246b0400d7Skarels 
825b7ce7f86Ssam /*
826b7ce7f86Ssam  * Process an ioctl request.
827b7ce7f86Ssam  */
82871555f1fSkarels /* ARGSUSED */
dmcioctl(ifp,cmd,data)829b7ce7f86Ssam dmcioctl(ifp, cmd, data)
830b7ce7f86Ssam 	register struct ifnet *ifp;
831b7ce7f86Ssam 	int cmd;
832b7ce7f86Ssam 	caddr_t data;
833b7ce7f86Ssam {
834b7ce7f86Ssam 	int s = splimp(), error = 0;
835f72a343aSkarels 	register struct dmc_softc *sc = &dmc_softc[ifp->if_unit];
836b7ce7f86Ssam 
837b7ce7f86Ssam 	switch (cmd) {
838b7ce7f86Ssam 
839b7ce7f86Ssam 	case SIOCSIFADDR:
840e8f34b3fStef 		ifp->if_flags |= IFF_UP;
841a08469b0Skarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
842a08469b0Skarels 			dmcinit(ifp->if_unit);
843b7ce7f86Ssam 		break;
844b7ce7f86Ssam 
845b7ce7f86Ssam 	case SIOCSIFDSTADDR:
846a08469b0Skarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
847a08469b0Skarels 			dmcinit(ifp->if_unit);
848b7ce7f86Ssam 		break;
849b7ce7f86Ssam 
850f72a343aSkarels 	case SIOCSIFFLAGS:
851f72a343aSkarels 		if ((ifp->if_flags & IFF_UP) == 0 &&
852d468a3c3Skarels 		    sc->sc_flag & DMC_RUNNING)
853d468a3c3Skarels 			dmcdown(ifp->if_unit);
854d468a3c3Skarels 		else if (ifp->if_flags & IFF_UP &&
855f72a343aSkarels 		    (sc->sc_flag & DMC_RUNNING) == 0)
856f72a343aSkarels 			dmcrestart(ifp->if_unit);
857f72a343aSkarels 		break;
858f72a343aSkarels 
859b7ce7f86Ssam 	default:
860b7ce7f86Ssam 		error = EINVAL;
861b7ce7f86Ssam 	}
862b7ce7f86Ssam 	splx(s);
863b7ce7f86Ssam 	return (error);
864b7ce7f86Ssam }
865e8f34b3fStef 
8666b0400d7Skarels /*
8676b0400d7Skarels  * Restart after a fatal error.
8686b0400d7Skarels  * Clear device and reinitialize.
8696b0400d7Skarels  */
dmcrestart(unit)8706b0400d7Skarels dmcrestart(unit)
8716b0400d7Skarels 	int unit;
8726b0400d7Skarels {
8736b0400d7Skarels 	register struct dmc_softc *sc = &dmc_softc[unit];
8746b0400d7Skarels 	register struct dmcdevice *addr;
8756b0400d7Skarels 	register int i;
876d468a3c3Skarels 	int s;
8776b0400d7Skarels 
8786b0400d7Skarels #ifdef DEBUG
8796b0400d7Skarels 	/* dump base table */
8806b0400d7Skarels 	printf("dmc%d base table:\n", unit);
8816b0400d7Skarels 	for (i = 0; i < sizeof (struct dmc_base); i++)
8826b0400d7Skarels 		printf("%o\n" ,dmc_base[unit].d_base[i]);
8836b0400d7Skarels #endif
884d468a3c3Skarels 
885d468a3c3Skarels 	dmcdown(unit);
886d468a3c3Skarels 
8876b0400d7Skarels 	/*
8886b0400d7Skarels 	 * Let the DMR finish the MCLR.	 At 1 Mbit, it should do so
8896b0400d7Skarels 	 * in about a max of 6.4 milliseconds with diagnostics enabled.
8906b0400d7Skarels 	 */
891d468a3c3Skarels 	addr = (struct dmcdevice *)(dmcinfo[unit]->ui_addr);
8926b0400d7Skarels 	for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--)
8936b0400d7Skarels 		;
8946b0400d7Skarels 	/* Did the timer expire or did the DMR finish? */
8956b0400d7Skarels 	if ((addr->bsel1 & DMC_RUN) == 0) {
89679130c04Skarels 		log(LOG_ERR, "dmc%d: M820 Test Failed\n", unit);
8976b0400d7Skarels 		return;
8986b0400d7Skarels 	}
8996b0400d7Skarels 
900d468a3c3Skarels 	/* restart DMC */
901d468a3c3Skarels 	dmcinit(unit);
902d468a3c3Skarels 	sc->sc_flag &= ~DMC_RESTART;
903d468a3c3Skarels 	s = spl5();
904d468a3c3Skarels 	dmcstart(unit);
905d468a3c3Skarels 	splx(s);
906d468a3c3Skarels 	sc->sc_if.if_collisions++;	/* why not? */
907d468a3c3Skarels }
908d468a3c3Skarels 
909d468a3c3Skarels /*
910d468a3c3Skarels  * Reset a device and mark down.
911d468a3c3Skarels  * Flush output queue and drop queue limit.
912d468a3c3Skarels  */
dmcdown(unit)913d468a3c3Skarels dmcdown(unit)
914d468a3c3Skarels 	int unit;
915d468a3c3Skarels {
916d468a3c3Skarels 	register struct dmc_softc *sc = &dmc_softc[unit];
917d468a3c3Skarels 	register struct ifxmt *ifxp;
918d468a3c3Skarels 
919d468a3c3Skarels 	((struct dmcdevice *)(dmcinfo[unit]->ui_addr))->bsel1 = DMC_MCLR;
920d468a3c3Skarels 	sc->sc_flag &= ~(DMC_RUNNING | DMC_ONLINE);
921d468a3c3Skarels 
92271555f1fSkarels 	for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) {
92371555f1fSkarels 		if (ifxp->ifw_xtofree) {
92471555f1fSkarels 			(void) m_freem(ifxp->ifw_xtofree);
92571555f1fSkarels 			ifxp->ifw_xtofree = 0;
9266b0400d7Skarels 		}
9276b0400d7Skarels 	}
928d468a3c3Skarels 	if_qflush(&sc->sc_if.if_snd);
9296b0400d7Skarels }
9306b0400d7Skarels 
9316b0400d7Skarels /*
932d468a3c3Skarels  * Watchdog timeout to see that transmitted packets don't
933d468a3c3Skarels  * lose interrupts.  The device has to be online (the first
934d468a3c3Skarels  * transmission may block until the other side comes up).
9356b0400d7Skarels  */
dmctimeout(unit)936d468a3c3Skarels dmctimeout(unit)
937d468a3c3Skarels 	int unit;
9386b0400d7Skarels {
9396b0400d7Skarels 	register struct dmc_softc *sc;
9406b0400d7Skarels 	struct dmcdevice *addr;
9416b0400d7Skarels 
942d468a3c3Skarels 	sc = &dmc_softc[unit];
943d468a3c3Skarels 	if (sc->sc_flag & DMC_ONLINE) {
944d468a3c3Skarels 		addr = (struct dmcdevice *)(dmcinfo[unit]->ui_addr);
945d468a3c3Skarels 		log(LOG_ERR, "dmc%d: output timeout, bsel0=%b bsel2=%b\n",
946d468a3c3Skarels 		    unit, addr->bsel0 & 0xff, DMC0BITS,
9476b0400d7Skarels 		    addr->bsel2 & 0xff, DMC2BITS);
948d468a3c3Skarels 		dmcrestart(unit);
9496b0400d7Skarels 	}
9506b0400d7Skarels }
951014ee908Ssam #endif
952