xref: /original-bsd/sys/vax/if/if_dmv.c (revision dd262573)
112a7ff8fSkarels /*
227e2a303Smckusick  * Copyright (c) 1988 Regents of the University of California.
327e2a303Smckusick  * All rights reserved.
427e2a303Smckusick  *
5defcbc46Sbostic  * %sccs.include.redist.c%
627e2a303Smckusick  *
7*dd262573Sbostic  *	@(#)if_dmv.c	7.12 (Berkeley) 12/16/90
822c2ba50Sbostic  */
922c2ba50Sbostic 
1022c2ba50Sbostic /*
1112a7ff8fSkarels  * DMV-11 Driver
1212a7ff8fSkarels  *
1312a7ff8fSkarels  * Qbus Sync DDCMP interface - DMV operated in full duplex, point to point mode
1412a7ff8fSkarels  *
1527e2a303Smckusick  * Written by Bob Kridle of Mt Xinu
1627e2a303Smckusick  * starting from if_dmc.c version 6.12 dated 4/23/86
1712a7ff8fSkarels  */
1812a7ff8fSkarels 
1912a7ff8fSkarels #include "dmv.h"
2012a7ff8fSkarels #if NDMV > 0
2112a7ff8fSkarels 
22*dd262573Sbostic #include "sys/param.h"
23*dd262573Sbostic #include "sys/systm.h"
24*dd262573Sbostic #include "sys/mbuf.h"
25*dd262573Sbostic #include "sys/buf.h"
26*dd262573Sbostic #include "sys/ioctl.h"		/* must precede tty.h */
27*dd262573Sbostic #include "sys/tty.h"
28*dd262573Sbostic #include "sys/protosw.h"
29*dd262573Sbostic #include "sys/socket.h"
30*dd262573Sbostic #include "sys/syslog.h"
31*dd262573Sbostic #include "sys/vmmac.h"
32*dd262573Sbostic #include "sys/errno.h"
33*dd262573Sbostic #include "sys/time.h"
34*dd262573Sbostic #include "sys/kernel.h"
3512a7ff8fSkarels 
36*dd262573Sbostic #include "net/if.h"
37*dd262573Sbostic #include "net/netisr.h"
38*dd262573Sbostic #include "net/route.h"
3912a7ff8fSkarels 
4012a7ff8fSkarels #ifdef	INET
41*dd262573Sbostic #include "netinet/in.h"
42*dd262573Sbostic #include "netinet/in_systm.h"
43*dd262573Sbostic #include "netinet/in_var.h"
44*dd262573Sbostic #include "netinet/ip.h"
4512a7ff8fSkarels #endif
4612a7ff8fSkarels 
47*dd262573Sbostic #include "../include/cpu.h"
48*dd262573Sbostic #include "../include/mtpr.h"
49*dd262573Sbostic #include "../include/pte.h"
50*dd262573Sbostic #include "../uba/ubareg.h"
51*dd262573Sbostic #include "../uba/ubavar.h"
52d468a3c3Skarels #include "if_uba.h"
53d468a3c3Skarels #include "if_dmv.h"
5412a7ff8fSkarels 
5512a7ff8fSkarels int	dmv_timeout = 8;		/* timeout value */
5612a7ff8fSkarels 
5712a7ff8fSkarels /*
5812a7ff8fSkarels  * Driver information for auto-configuration stuff.
5912a7ff8fSkarels  */
6012a7ff8fSkarels int	dmvprobe(), dmvattach(), dmvinit(), dmvioctl();
61d468a3c3Skarels int	dmvoutput(), dmvreset(), dmvtimeout();
6212a7ff8fSkarels struct	uba_device *dmvinfo[NDMV];
6312a7ff8fSkarels u_short	dmvstd[] = { 0 };
6412a7ff8fSkarels struct	uba_driver dmvdriver =
6512a7ff8fSkarels 	{ dmvprobe, 0, dmvattach, 0, dmvstd, "dmv", dmvinfo };
6612a7ff8fSkarels 
6712a7ff8fSkarels /*
6812a7ff8fSkarels  * Don't really know how many buffers/commands can be queued to a DMV-11.
6912a7ff8fSkarels  * Manual doesn't say... Perhaps we can look at a DEC driver some day.
70d468a3c3Skarels  * These numbers ame from DMC/DMR driver.
7112a7ff8fSkarels  */
7212a7ff8fSkarels #define NRCV 5
7312a7ff8fSkarels #define NXMT 3
7412a7ff8fSkarels #define NCMDS	(NRCV+NXMT+4)	/* size of command queue */
7512a7ff8fSkarels 
76d468a3c3Skarels #ifdef DEBUG
77d468a3c3Skarels #define printd(f)   if (sc->sc_if.if_flags & IFF_DEBUG) \
78d468a3c3Skarels 	printf("DMVDEBUG: dmv%d: ", unit), printf(f)
79d468a3c3Skarels #else
80d468a3c3Skarels #define	printd(f)	/* nil */
81d468a3c3Skarels #endif
8212a7ff8fSkarels 
8312a7ff8fSkarels /* error reporting intervals */
8412a7ff8fSkarels 
8512a7ff8fSkarels #define	DMV_RPRTE	 1
8612a7ff8fSkarels #define	DMV_RPTTE        1
8712a7ff8fSkarels #define	DMV_RPSTE	 1
8812a7ff8fSkarels #define DMV_RPNXM        1
8912a7ff8fSkarels #define DMV_RPMODD       1
9012a7ff8fSkarels #define DMV_RPQOVF	 1
9112a7ff8fSkarels #define DMV_RPCXRL	 1
9257dda7d5Skarels 
9357dda7d5Skarels /* number of errors to accept before trying a reset */
9457dda7d5Skarels #define DMV_RPUNKNOWN	 10
9512a7ff8fSkarels 
9612a7ff8fSkarels struct  dmv_command {
9712a7ff8fSkarels 	u_char	qp_mask;	/* Which registers to set up */
9812a7ff8fSkarels #define	QP_TRIB		0x01
9912a7ff8fSkarels #define	QP_SEL4		0x02
10012a7ff8fSkarels #define	QP_SEL6		0x04
10112a7ff8fSkarels #define	QP_SEL10	0x08
10212a7ff8fSkarels 	u_char	qp_cmd;
10312a7ff8fSkarels 	u_char	qp_tributary;
10412a7ff8fSkarels 	u_short	qp_sel4;
10512a7ff8fSkarels 	u_short	qp_sel6;
10612a7ff8fSkarels 	u_short	qp_sel10;
10712a7ff8fSkarels 	struct	dmv_command *qp_next;	/* next command on queue */
10812a7ff8fSkarels };
10912a7ff8fSkarels 
11012a7ff8fSkarels #define	qp_lowbufaddr	qp_
11112a7ff8fSkarels 
11212a7ff8fSkarels struct dmvbufs {
11312a7ff8fSkarels 	int	ubinfo;		/* from uballoc */
11412a7ff8fSkarels 	short	cc;		/* buffer size */
11512a7ff8fSkarels 	short	flags;		/* access control */
11612a7ff8fSkarels };
11712a7ff8fSkarels 
11812a7ff8fSkarels #define	DBUF_OURS	0	/* buffer is available */
11912a7ff8fSkarels #define	DBUF_DMVS	1	/* buffer claimed by somebody */
12012a7ff8fSkarels #define	DBUF_XMIT	4	/* transmit buffer */
12112a7ff8fSkarels #define	DBUF_RCV	8	/* receive buffer */
12212a7ff8fSkarels 
12312a7ff8fSkarels 
12412a7ff8fSkarels /*
12512a7ff8fSkarels  * DMV software status per interface.
12612a7ff8fSkarels  *
12712a7ff8fSkarels  * Each interface is referenced by a network interface structure,
12812a7ff8fSkarels  * sc_if, which the routing code uses to locate the interface.
12912a7ff8fSkarels  * This structure contains the output queue for the interface, its address, ...
13012a7ff8fSkarels  * We also have, for each interface, a  set of 7 UBA interface structures
13112a7ff8fSkarels  * for each, which
13212a7ff8fSkarels  * contain information about the UNIBUS resources held by the interface:
13312a7ff8fSkarels  * map registers, buffered data paths, etc.  Information is cached in this
13412a7ff8fSkarels  * structure for use by the if_uba.c routines in running the interface
13512a7ff8fSkarels  * efficiently.
13612a7ff8fSkarels  */
13712a7ff8fSkarels struct dmv_softc {
13812a7ff8fSkarels 	struct	ifnet sc_if;		/* network-visible interface */
13912a7ff8fSkarels 	short	sc_oused;		/* output buffers currently in use */
14012a7ff8fSkarels 	short	sc_iused;		/* input buffers given to DMV */
14112a7ff8fSkarels 	short	sc_flag;		/* flags */
142e578e9bdSkarels 	short	sc_ipl;			/* interrupt priority */
14312a7ff8fSkarels 	int	sc_ubinfo;		/* UBA mapping info for base table */
14412a7ff8fSkarels 	int	sc_errors[8];		/* error counters */
14512a7ff8fSkarels #define	sc_rte	sc_errors[0]		/* receive threshhold error */
14612a7ff8fSkarels #define	sc_xte	sc_errors[1]		/* xmit threshhold error */
14712a7ff8fSkarels #define	sc_ste	sc_errors[2]		/* select threshhold error */
14812a7ff8fSkarels #define	sc_nxm	sc_errors[3]		/* non-existant memory */
14912a7ff8fSkarels #define	sc_modd	sc_errors[4]		/* modem disconnect */
15012a7ff8fSkarels #define	sc_qovf	sc_errors[5]		/* command/response queue overflow */
15112a7ff8fSkarels #define	sc_cxrl	sc_errors[6]		/* carrier loss */
15212a7ff8fSkarels #define sc_unknown sc_errors[7]		/* other errors - look in DMV manual */
153d468a3c3Skarels 	struct	dmvbufs sc_rbufs[NRCV];	/* receive buffer info */
154d468a3c3Skarels 	struct	dmvbufs sc_xbufs[NXMT];	/* transmit buffer info */
155d468a3c3Skarels 	struct	ifubinfo sc_ifuba;	/* UNIBUS resources */
156d468a3c3Skarels 	struct	ifrw sc_ifr[NRCV];	/* UNIBUS receive buffer maps */
157d468a3c3Skarels 	struct	ifxmt sc_ifw[NXMT];	/* UNIBUS receive buffer maps */
15812a7ff8fSkarels 	/* command queue stuff */
15912a7ff8fSkarels 	struct	dmv_command sc_cmdbuf[NCMDS];
16012a7ff8fSkarels 	struct	dmv_command *sc_qhead;	/* head of command queue */
16112a7ff8fSkarels 	struct	dmv_command *sc_qtail;	/* tail of command queue */
16212a7ff8fSkarels 	struct	dmv_command *sc_qactive;	/* command in progress */
16312a7ff8fSkarels 	struct	dmv_command *sc_qfreeh;	/* head of list of free cmd buffers */
16412a7ff8fSkarels 	struct	dmv_command *sc_qfreet;	/* tail of list of free cmd buffers */
16512a7ff8fSkarels 	/* end command queue stuff */
16612a7ff8fSkarels } dmv_softc[NDMV];
16712a7ff8fSkarels 
16812a7ff8fSkarels /* flags */
169d468a3c3Skarels #define DMV_RESTART	0x01		/* software restart in progress */
170d468a3c3Skarels #define DMV_ONLINE	0x02		/* device managed to transmit */
171d468a3c3Skarels #define DMV_RUNNING	0x04		/* device initialized */
17212a7ff8fSkarels 
17312a7ff8fSkarels 
17412a7ff8fSkarels /* queue manipulation macros */
17512a7ff8fSkarels #define	QUEUE_AT_HEAD(qp, head, tail) \
17612a7ff8fSkarels 	(qp)->qp_next = (head); \
17712a7ff8fSkarels 	(head) = (qp); \
17812a7ff8fSkarels 	if ((tail) == (struct dmv_command *) 0) \
17912a7ff8fSkarels 		(tail) = (head)
18012a7ff8fSkarels 
18112a7ff8fSkarels #define QUEUE_AT_TAIL(qp, head, tail) \
18212a7ff8fSkarels 	if ((tail)) \
18312a7ff8fSkarels 		(tail)->qp_next = (qp); \
18412a7ff8fSkarels 	else \
18512a7ff8fSkarels 		(head) = (qp); \
18612a7ff8fSkarels 	(qp)->qp_next = (struct dmv_command *) 0; \
18712a7ff8fSkarels 	(tail) = (qp)
18812a7ff8fSkarels 
18912a7ff8fSkarels #define DEQUEUE(head, tail) \
19012a7ff8fSkarels 	(head) = (head)->qp_next;\
19112a7ff8fSkarels 	if ((head) == (struct dmv_command *) 0)\
19212a7ff8fSkarels 		(tail) = (head)
19312a7ff8fSkarels 
dmvprobe(reg,ui)194e578e9bdSkarels dmvprobe(reg, ui)
19512a7ff8fSkarels 	caddr_t reg;
196e578e9bdSkarels 	struct uba_device *ui;
19712a7ff8fSkarels {
19812a7ff8fSkarels 	register int br, cvec;
19912a7ff8fSkarels 	register struct dmvdevice *addr = (struct dmvdevice *)reg;
20012a7ff8fSkarels 	register int i;
20112a7ff8fSkarels 
20212a7ff8fSkarels #ifdef lint
20312a7ff8fSkarels 	br = 0; cvec = br; br = cvec;
20412a7ff8fSkarels 	dmvrint(0); dmvxint(0);
20512a7ff8fSkarels #endif
20612a7ff8fSkarels 	addr->bsel1 = DMV_MCLR;
20712a7ff8fSkarels 	for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--)
20812a7ff8fSkarels 		;
20912a7ff8fSkarels 	if ((addr->bsel1 & DMV_RUN) == 0) {
21012a7ff8fSkarels 		printf("dmvprobe: can't start device\n" );
21112a7ff8fSkarels 		return (0);
21212a7ff8fSkarels 	}
21312a7ff8fSkarels 	if ((addr->bsel4 != 033) || (addr->bsel6 != 0305))
21412a7ff8fSkarels 	{
21512a7ff8fSkarels 		printf("dmvprobe: device init failed, bsel4=%o, bsel6=%o\n",
21612a7ff8fSkarels 			addr->bsel4, addr->bsel6);
21712a7ff8fSkarels 		return (0);
21812a7ff8fSkarels 	}
219e578e9bdSkarels 	(void) spl6();
22012a7ff8fSkarels 	addr->bsel0 = DMV_RQI|DMV_IEI|DMV_IEO;
22112a7ff8fSkarels 	DELAY(1000000);
2228f3beb79Skarels 	dmv_softc[ui->ui_unit].sc_ipl = br = qbgetpri();
22312a7ff8fSkarels 	addr->bsel1 = DMV_MCLR;
22412a7ff8fSkarels 	for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--)
22512a7ff8fSkarels 		;
226e578e9bdSkarels 	return (sizeof(struct dmvdevice));
22712a7ff8fSkarels }
22812a7ff8fSkarels 
22912a7ff8fSkarels /*
23012a7ff8fSkarels  * Interface exists: make available by filling in network interface
23112a7ff8fSkarels  * record.  System will initialize the interface when it is ready
23212a7ff8fSkarels  * to accept packets.
23312a7ff8fSkarels  */
dmvattach(ui)23412a7ff8fSkarels dmvattach(ui)
23512a7ff8fSkarels 	register struct uba_device *ui;
23612a7ff8fSkarels {
23712a7ff8fSkarels 	register struct dmv_softc *sc = &dmv_softc[ui->ui_unit];
23812a7ff8fSkarels 
23912a7ff8fSkarels 	sc->sc_if.if_unit = ui->ui_unit;
24012a7ff8fSkarels 	sc->sc_if.if_name = "dmv";
24112a7ff8fSkarels 	sc->sc_if.if_mtu = DMVMTU;
24212a7ff8fSkarels 	sc->sc_if.if_init = dmvinit;
24312a7ff8fSkarels 	sc->sc_if.if_output = dmvoutput;
24412a7ff8fSkarels 	sc->sc_if.if_ioctl = dmvioctl;
24512a7ff8fSkarels 	sc->sc_if.if_reset = dmvreset;
246d468a3c3Skarels 	sc->sc_if.if_watchdog = dmvtimeout;
24712a7ff8fSkarels 	sc->sc_if.if_flags = IFF_POINTOPOINT;
24812a7ff8fSkarels 	sc->sc_ifuba.iff_flags = UBA_CANTWAIT;
24912a7ff8fSkarels 
25012a7ff8fSkarels 	if_attach(&sc->sc_if);
25112a7ff8fSkarels }
25212a7ff8fSkarels 
25312a7ff8fSkarels /*
25412a7ff8fSkarels  * Reset of interface after UNIBUS reset.
25512a7ff8fSkarels  * If interface is on specified UBA, reset its state.
25612a7ff8fSkarels  */
dmvreset(unit,uban)25712a7ff8fSkarels dmvreset(unit, uban)
25812a7ff8fSkarels 	int unit, uban;
25912a7ff8fSkarels {
26012a7ff8fSkarels 	register struct uba_device *ui;
26112a7ff8fSkarels 	register struct dmv_softc *sc = &dmv_softc[unit];
26212a7ff8fSkarels 
26312a7ff8fSkarels 	if (unit >= NDMV || (ui = dmvinfo[unit]) == 0 || ui->ui_alive == 0 ||
26412a7ff8fSkarels 	    ui->ui_ubanum != uban)
26512a7ff8fSkarels 		return;
26612a7ff8fSkarels 	printf(" dmv%d", unit);
26712a7ff8fSkarels 	sc->sc_flag = 0;
26812a7ff8fSkarels 	sc->sc_if.if_flags &= ~IFF_RUNNING;
26912a7ff8fSkarels 	dmvinit(unit);
27012a7ff8fSkarels }
27112a7ff8fSkarels 
27212a7ff8fSkarels /*
27312a7ff8fSkarels  * Initialization of interface; reinitialize UNIBUS usage.
27412a7ff8fSkarels  */
dmvinit(unit)27512a7ff8fSkarels dmvinit(unit)
27612a7ff8fSkarels 	int unit;
27712a7ff8fSkarels {
27812a7ff8fSkarels 	register struct dmv_softc *sc = &dmv_softc[unit];
27912a7ff8fSkarels 	register struct uba_device *ui = dmvinfo[unit];
28012a7ff8fSkarels 	register struct dmvdevice *addr;
28112a7ff8fSkarels 	register struct ifnet *ifp = &sc->sc_if;
28212a7ff8fSkarels 	register struct ifrw *ifrw;
28312a7ff8fSkarels 	register struct ifxmt *ifxp;
28412a7ff8fSkarels 	register struct dmvbufs *rp;
28512a7ff8fSkarels 	register struct dmv_command *qp;
28612a7ff8fSkarels 	struct ifaddr *ifa;
28712a7ff8fSkarels 	int base;
28812a7ff8fSkarels 	int s;
28912a7ff8fSkarels 
29012a7ff8fSkarels 	addr = (struct dmvdevice *)ui->ui_addr;
29112a7ff8fSkarels 
29212a7ff8fSkarels 	/*
29312a7ff8fSkarels 	 * Check to see that an address has been set
29412a7ff8fSkarels 	 * (both local and destination for an address family).
29512a7ff8fSkarels 	 */
29612a7ff8fSkarels 	for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
29701f0d4cbSsklower 		if (ifa->ifa_addr->sa_family &&
29801f0d4cbSsklower 		    ifa->ifa_addr->sa_family != AF_LINK &&
29901f0d4cbSsklower 		    ifa->ifa_dstaddr && ifa->ifa_dstaddr->sa_family)
30012a7ff8fSkarels 			break;
30112a7ff8fSkarels 	if (ifa == (struct ifaddr *) 0)
30212a7ff8fSkarels 		return;
30312a7ff8fSkarels 
30412a7ff8fSkarels 	if ((addr->bsel1&DMV_RUN) == 0) {
30512a7ff8fSkarels 		log(LOG_CRIT, "dmvinit: dmv%d not running\n", unit);
30612a7ff8fSkarels 		ifp->if_flags &= ~IFF_UP;
30712a7ff8fSkarels 		return;
30812a7ff8fSkarels 	}
309d468a3c3Skarels 	printd(("dmvinit\n"));
31012a7ff8fSkarels 	/* initialize UNIBUS resources */
31112a7ff8fSkarels 	sc->sc_iused = sc->sc_oused = 0;
31212a7ff8fSkarels 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
31312a7ff8fSkarels 		if (if_ubaminit(
31412a7ff8fSkarels 			&sc->sc_ifuba,
31512a7ff8fSkarels 			ui->ui_ubanum,
31612a7ff8fSkarels 		    	sizeof(struct dmv_header),
31712a7ff8fSkarels 			(int)btoc(DMVMTU),
31812a7ff8fSkarels 			sc->sc_ifr,
31912a7ff8fSkarels 			NRCV,
32012a7ff8fSkarels 			sc->sc_ifw,
32112a7ff8fSkarels 			NXMT
32212a7ff8fSkarels 	      	) == 0) {
32312a7ff8fSkarels 			log(LOG_CRIT, "dmvinit: dmv%d can't allocate uba resources\n", unit);
32412a7ff8fSkarels 			ifp->if_flags &= ~IFF_UP;
32512a7ff8fSkarels 			return;
32612a7ff8fSkarels 		}
32712a7ff8fSkarels 		ifp->if_flags |= IFF_RUNNING;
32812a7ff8fSkarels 	}
329d468a3c3Skarels 	/*
330d468a3c3Skarels 	 * Limit packets enqueued until we see if we're on the air.
331d468a3c3Skarels 	 */
332d468a3c3Skarels 	ifp->if_snd.ifq_maxlen = 3;
333d468a3c3Skarels 
33412a7ff8fSkarels 
33512a7ff8fSkarels 	/* initialize buffer pool */
33612a7ff8fSkarels 	/* receives */
33712a7ff8fSkarels 	ifrw = &sc->sc_ifr[0];
33812a7ff8fSkarels 	for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
339e578e9bdSkarels 		rp->ubinfo = UBAI_ADDR(ifrw->ifrw_info);
34012a7ff8fSkarels 		rp->cc = DMVMTU + sizeof (struct dmv_header);
34112a7ff8fSkarels 		rp->flags = DBUF_OURS|DBUF_RCV;
34212a7ff8fSkarels 		ifrw++;
34312a7ff8fSkarels 	}
34412a7ff8fSkarels 	/* transmits */
34512a7ff8fSkarels 	ifxp = &sc->sc_ifw[0];
34612a7ff8fSkarels 	for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
347e578e9bdSkarels 		rp->ubinfo = UBAI_ADDR(ifxp->ifw_info);
34812a7ff8fSkarels 		rp->cc = 0;
34912a7ff8fSkarels 		rp->flags = DBUF_OURS|DBUF_XMIT;
35012a7ff8fSkarels 		ifxp++;
35112a7ff8fSkarels 	}
35212a7ff8fSkarels 
35312a7ff8fSkarels 	/* set up command queues */
35412a7ff8fSkarels 	sc->sc_qfreeh = sc->sc_qfreet
35512a7ff8fSkarels 		 = sc->sc_qhead = sc->sc_qtail = sc->sc_qactive =
35612a7ff8fSkarels 		(struct dmv_command *)0;
35712a7ff8fSkarels 	/* set up free command buffer list */
35812a7ff8fSkarels 	for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) {
35912a7ff8fSkarels 		QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
36012a7ff8fSkarels 	}
36112a7ff8fSkarels 	if(sc->sc_flag & DMV_RUNNING)
36212a7ff8fSkarels 		dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_REQHS,0);
36312a7ff8fSkarels 	else
36412a7ff8fSkarels 		dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_ESTTRIB,0);
36512a7ff8fSkarels 	dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_REQSUS,0);
36612a7ff8fSkarels 	sc->sc_flag |= (DMV_RESTART|DMV_RUNNING);
367d468a3c3Skarels 	sc->sc_flag &= ~DMV_ONLINE;
36812a7ff8fSkarels 	addr->bsel0 |= DMV_IEO;
36912a7ff8fSkarels }
37012a7ff8fSkarels 
37112a7ff8fSkarels /*
37212a7ff8fSkarels  * Start output on interface.  Get another datagram
37312a7ff8fSkarels  * to send from the interface queue and map it to
37412a7ff8fSkarels  * the interface before starting output.
37512a7ff8fSkarels  *
37612a7ff8fSkarels  * Must be called at spl 5
37712a7ff8fSkarels  */
dmvstart(dev)37812a7ff8fSkarels dmvstart(dev)
37912a7ff8fSkarels 	dev_t dev;
38012a7ff8fSkarels {
38112a7ff8fSkarels 	int unit = minor(dev);
38212a7ff8fSkarels 	register struct dmv_softc *sc = &dmv_softc[unit];
38312a7ff8fSkarels 	struct mbuf *m;
38412a7ff8fSkarels 	register struct dmvbufs *rp;
38512a7ff8fSkarels 	register int n;
38612a7ff8fSkarels 
38712a7ff8fSkarels 	/*
38812a7ff8fSkarels 	 * Dequeue up to NXMT requests and map them to the UNIBUS.
38912a7ff8fSkarels 	 * If no more requests, or no dmv buffers available, just return.
39012a7ff8fSkarels 	 */
391d468a3c3Skarels 	printd(("dmvstart\n"));
39212a7ff8fSkarels 	n = 0;
39312a7ff8fSkarels 	for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) {
39412a7ff8fSkarels 		/* find an available buffer */
39512a7ff8fSkarels 		if ((rp->flags & DBUF_DMVS) == 0) {
39612a7ff8fSkarels 			IF_DEQUEUE(&sc->sc_if.if_snd, m);
39712a7ff8fSkarels 			if (m == 0)
39812a7ff8fSkarels 				return;
39912a7ff8fSkarels 			/* mark it dmvs */
40012a7ff8fSkarels 			rp->flags |= (DBUF_DMVS);
40112a7ff8fSkarels 			/*
40212a7ff8fSkarels 			 * Have request mapped to UNIBUS for transmission
40312a7ff8fSkarels 			 * and start the output.
40412a7ff8fSkarels 			 */
40512a7ff8fSkarels 			rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m);
406d468a3c3Skarels 			if (++sc->sc_oused == 1)
407d468a3c3Skarels 				sc->sc_if.if_timer = dmv_timeout;
40812a7ff8fSkarels 			dmvload(
40912a7ff8fSkarels 				sc,
41012a7ff8fSkarels 				DMV_BACCX,
41112a7ff8fSkarels 				QP_TRIB|QP_SEL4|QP_SEL6|QP_SEL10,
41212a7ff8fSkarels 				1,
41312a7ff8fSkarels 				rp->ubinfo,
41412a7ff8fSkarels 				(rp->ubinfo>>16)&0x3f,
41512a7ff8fSkarels 				rp->cc
41612a7ff8fSkarels 			);
41712a7ff8fSkarels 		}
41812a7ff8fSkarels 		n++;
41912a7ff8fSkarels 	}
42012a7ff8fSkarels }
42112a7ff8fSkarels 
42212a7ff8fSkarels /*
42312a7ff8fSkarels  * Utility routine to load the DMV device registers.
42412a7ff8fSkarels  */
dmvload(sc,cmd,mask,tributary,sel4,sel6,sel10)42512a7ff8fSkarels dmvload(sc, cmd, mask, tributary, sel4, sel6, sel10)
42612a7ff8fSkarels 	register struct dmv_softc *sc;
42712a7ff8fSkarels 	u_char cmd, tributary, mask;
42812a7ff8fSkarels 	u_short sel4, sel6, sel10;
42912a7ff8fSkarels {
43012a7ff8fSkarels 	register struct dmvdevice *addr;
43112a7ff8fSkarels 	register int unit, sps;
43212a7ff8fSkarels 	register struct dmv_command *qp;
43312a7ff8fSkarels 
43412a7ff8fSkarels 	unit = sc - dmv_softc;
435d468a3c3Skarels 	printd(("dmvload: cmd=%x mask=%x trib=%x sel4=%x sel6=%x sel10=%x\n",
43612a7ff8fSkarels 		(unsigned) cmd,
43712a7ff8fSkarels 		(unsigned) mask,
43812a7ff8fSkarels 		(unsigned) tributary,
43912a7ff8fSkarels 		(unsigned) sel4,
44012a7ff8fSkarels 		(unsigned) sel6,
44112a7ff8fSkarels 		(unsigned) sel10
442d468a3c3Skarels 	));
44312a7ff8fSkarels 	addr = (struct dmvdevice *)dmvinfo[unit]->ui_addr;
44412a7ff8fSkarels 	sps = spl5();
44512a7ff8fSkarels 
44612a7ff8fSkarels 	/* grab a command buffer from the free list */
44712a7ff8fSkarels 	if ((qp = sc->sc_qfreeh) == (struct dmv_command *)0)
44812a7ff8fSkarels 		panic("dmv command queue overflow");
44912a7ff8fSkarels 	DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet);
45012a7ff8fSkarels 
45112a7ff8fSkarels 	/* fill in requested info */
45212a7ff8fSkarels 	qp->qp_cmd = cmd;
45312a7ff8fSkarels 	qp->qp_mask = mask;
45412a7ff8fSkarels 	qp->qp_tributary = tributary;
45512a7ff8fSkarels 	qp->qp_sel4 = sel4;
45612a7ff8fSkarels 	qp->qp_sel6 = sel6;
45712a7ff8fSkarels 	qp->qp_sel10 = sel10;
45812a7ff8fSkarels 
45912a7ff8fSkarels 	if (sc->sc_qactive) {	/* command in progress */
46012a7ff8fSkarels 		if (cmd == DMV_BACCR) {  /* supply read buffers first */
46112a7ff8fSkarels 			QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail);
46212a7ff8fSkarels 		} else {
46312a7ff8fSkarels 			QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail);
46412a7ff8fSkarels 		}
46512a7ff8fSkarels 	} else {	/* command port free */
46612a7ff8fSkarels 		sc->sc_qactive = qp;
46712a7ff8fSkarels 		addr->bsel0 = (DMV_RQI|DMV_IEI|DMV_IEO);
46812a7ff8fSkarels 	}
46912a7ff8fSkarels 	splx(sps);
47012a7ff8fSkarels }
47112a7ff8fSkarels /*
47212a7ff8fSkarels  * DMV interface input interrupt.
47312a7ff8fSkarels  * Ready to accept another command,
47412a7ff8fSkarels  * pull one off the command queue.
47512a7ff8fSkarels  */
dmvrint(unit)47612a7ff8fSkarels dmvrint(unit)
47712a7ff8fSkarels 	int unit;
47812a7ff8fSkarels {
47912a7ff8fSkarels 	register struct dmv_softc *sc;
48012a7ff8fSkarels 	register struct dmvdevice *addr;
48112a7ff8fSkarels 	register struct dmv_command *qp;
48212a7ff8fSkarels 	register int n;
48312a7ff8fSkarels 
48412a7ff8fSkarels 	addr = (struct dmvdevice *)dmvinfo[unit]->ui_addr;
48512a7ff8fSkarels 	sc = &dmv_softc[unit];
486e578e9bdSkarels 	splx(sc->sc_ipl);
487d468a3c3Skarels 	printd(("dmvrint\n"));
48812a7ff8fSkarels 	if ((qp = sc->sc_qactive) == (struct dmv_command *) 0) {
48912a7ff8fSkarels 		log(LOG_WARNING, "dmvrint: dmv%d no command\n", unit);
49012a7ff8fSkarels 		return;
49112a7ff8fSkarels 	}
49212a7ff8fSkarels 	while (addr->bsel2&DMV_RDI) {
49312a7ff8fSkarels 		if(qp->qp_mask&QP_SEL4)
49412a7ff8fSkarels 			addr->wsel4 = qp->qp_sel4;
49512a7ff8fSkarels 		if(qp->qp_mask&QP_SEL6)
49612a7ff8fSkarels 			addr->wsel6 = qp->qp_sel6;
49712a7ff8fSkarels 		if(qp->qp_mask&QP_SEL10) {
49812a7ff8fSkarels 			addr->wsel10 = qp->qp_sel10;
49912a7ff8fSkarels 			qp->qp_cmd |= DMV_22BIT;
50012a7ff8fSkarels 		}
50112a7ff8fSkarels 		if(qp->qp_mask&QP_TRIB)
50212a7ff8fSkarels 			addr->wsel2 = qp->qp_cmd|(qp->qp_tributary << 8);
50312a7ff8fSkarels 		else
50412a7ff8fSkarels 			addr->bsel2 = qp->qp_cmd;
50512a7ff8fSkarels 		QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
50612a7ff8fSkarels 		if ((sc->sc_qactive = sc->sc_qhead) == (struct dmv_command *)0)
50712a7ff8fSkarels 			break;
50812a7ff8fSkarels 		qp = sc->sc_qactive;
50912a7ff8fSkarels 		DEQUEUE(sc->sc_qhead, sc->sc_qtail);
51012a7ff8fSkarels 		if (addr->bsel2&DMV_RDO)
51112a7ff8fSkarels 				break;
51212a7ff8fSkarels 	}
51312a7ff8fSkarels 	if (!sc->sc_qactive) {
51412a7ff8fSkarels 		if(addr->bsel2&DMV_RDI) {
51512a7ff8fSkarels 			/* clear RQI prior to last command per DMV manual */
51612a7ff8fSkarels 			addr->bsel0 &= ~DMV_RQI;
51712a7ff8fSkarels 			addr->wsel6 = DMV_NOP;
51812a7ff8fSkarels 			addr->bsel2 = DMV_CNTRLI;
51912a7ff8fSkarels 		}
52012a7ff8fSkarels 		addr->bsel0 = DMV_IEO;
52112a7ff8fSkarels 	}
52212a7ff8fSkarels 	else /* RDO set or DMV still holding CSR */
52312a7ff8fSkarels 		addr->bsel0 = (DMV_RQI|DMV_IEI|DMV_IEO);
52412a7ff8fSkarels 
52512a7ff8fSkarels }
52612a7ff8fSkarels 
52712a7ff8fSkarels /*
52812a7ff8fSkarels  * DMV interface output interrupt.
52912a7ff8fSkarels  * A transfer may have completed, check for errors.
53012a7ff8fSkarels  * If it was a read, notify appropriate protocol.
53112a7ff8fSkarels  * If it was a write, pull the next one off the queue.
53212a7ff8fSkarels  */
dmvxint(unit)53312a7ff8fSkarels dmvxint(unit)
53412a7ff8fSkarels 	int unit;
53512a7ff8fSkarels {
53612a7ff8fSkarels 	register struct dmv_softc *sc;
53712a7ff8fSkarels 	register struct ifnet *ifp;
53812a7ff8fSkarels 	struct uba_device *ui = dmvinfo[unit];
53912a7ff8fSkarels 	struct dmvdevice *addr;
54012a7ff8fSkarels 	struct mbuf *m;
54112a7ff8fSkarels 	struct ifqueue *inq;
54212a7ff8fSkarels 	int sel2, sel3, sel4, sel6, sel10, pkaddr, len, s;
54312a7ff8fSkarels 	register struct ifrw *ifrw;
54412a7ff8fSkarels 	register struct dmvbufs *rp;
54512a7ff8fSkarels 	register struct ifxmt *ifxp;
54612a7ff8fSkarels 	struct dmv_header *dh;
547d468a3c3Skarels 	int off, resid;
54812a7ff8fSkarels 
54912a7ff8fSkarels 	addr = (struct dmvdevice *)ui->ui_addr;
55012a7ff8fSkarels 	sc = &dmv_softc[unit];
551e578e9bdSkarels 	splx(sc->sc_ipl);
55212a7ff8fSkarels 	ifp = &sc->sc_if;
55312a7ff8fSkarels 
55412a7ff8fSkarels 	while (addr->bsel2 & DMV_RDO) {
55512a7ff8fSkarels 
55612a7ff8fSkarels 		sel2 = addr->bsel2;
55712a7ff8fSkarels 		sel3 = addr->bsel3;
55812a7ff8fSkarels 		sel4 = addr->wsel4;		/* release port */
55912a7ff8fSkarels 		sel6 = addr->wsel6;
56012a7ff8fSkarels 		if(sel2 & DMV_22BIT)
56112a7ff8fSkarels 			sel10 = addr->wsel10;
56212a7ff8fSkarels 		addr->bsel2 &= ~DMV_RDO;
56312a7ff8fSkarels 		pkaddr =  sel4 | ((sel6 & 0x3f) << 16);
564d468a3c3Skarels 		printd(("dmvxint: sel2=%x sel4=%x sel6=%x sel10=%x pkaddr=%x\n",
56512a7ff8fSkarels 			(unsigned) sel2,
56612a7ff8fSkarels 			(unsigned) sel4,
56712a7ff8fSkarels 			(unsigned) sel6,
56812a7ff8fSkarels 			(unsigned) sel10,
56912a7ff8fSkarels 			(unsigned) pkaddr
570d468a3c3Skarels 		));
57112a7ff8fSkarels 		if((sc->sc_flag & DMV_RUNNING)==0) {
57212a7ff8fSkarels 				log(LOG_WARNING, "dmvxint: dmv%d xint while down\n", unit);
57312a7ff8fSkarels 				return;
57412a7ff8fSkarels 		}
57512a7ff8fSkarels 		switch (sel2 & 07) {
57612a7ff8fSkarels 		case DMV_BDRUS:
57712a7ff8fSkarels 			/*
57812a7ff8fSkarels 			 * A read has completed.
57912a7ff8fSkarels 			 * Pass packet to type specific
58012a7ff8fSkarels 			 * higher-level input routine.
58112a7ff8fSkarels 			 */
58212a7ff8fSkarels 			ifp->if_ipackets++;
58312a7ff8fSkarels 			/* find location in dmvuba struct */
58412a7ff8fSkarels 			ifrw= &sc->sc_ifr[0];
58512a7ff8fSkarels 			for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
58612a7ff8fSkarels 				if(rp->ubinfo == pkaddr)
58712a7ff8fSkarels 					break;
58812a7ff8fSkarels 				ifrw++;
58912a7ff8fSkarels 			}
59012a7ff8fSkarels 			if (rp >= &sc->sc_rbufs[NRCV])
59112a7ff8fSkarels 				panic("dmv rcv");
59212a7ff8fSkarels 			if ((rp->flags & DBUF_DMVS) == 0)
59312a7ff8fSkarels 				log(LOG_WARNING, "dmvxint: dmv%d done unalloc rbuf\n", unit);
59412a7ff8fSkarels 
59512a7ff8fSkarels 			len = (sel10&0x3fff) - sizeof (struct dmv_header);
59612a7ff8fSkarels 			if (len < 0 || len > DMVMTU) {
59712a7ff8fSkarels 				ifp->if_ierrors++;
59812a7ff8fSkarels 				log(LOG_ERR, "dmvxint: dmv%d bad rcv pkt addr 0x%x len 0x%x\n",
59912a7ff8fSkarels 				    unit, pkaddr, len);
60012a7ff8fSkarels 				goto setup;
60112a7ff8fSkarels 			}
60212a7ff8fSkarels 			/*
60312a7ff8fSkarels 			 * Deal with trailer protocol: if type is trailer
60412a7ff8fSkarels 			 * get true type from first 16-bit word past data.
60512a7ff8fSkarels 			 * Remember that type was trailer by setting off.
60612a7ff8fSkarels 			 */
60712a7ff8fSkarels 			dh = (struct dmv_header *)ifrw->ifrw_addr;
60812a7ff8fSkarels 			dh->dmv_type = ntohs((u_short)dh->dmv_type);
60912a7ff8fSkarels #define dmvdataaddr(dh, off, type)	((type)(((caddr_t)((dh)+1)+(off))))
61012a7ff8fSkarels 			if (dh->dmv_type >= DMV_TRAILER &&
61112a7ff8fSkarels 			    dh->dmv_type < DMV_TRAILER+DMV_NTRAILER) {
61212a7ff8fSkarels 				off = (dh->dmv_type - DMV_TRAILER) * 512;
61312a7ff8fSkarels 				if (off >= DMVMTU)
61412a7ff8fSkarels 					goto setup;		/* sanity */
61512a7ff8fSkarels 				dh->dmv_type = ntohs(*dmvdataaddr(dh, off, u_short *));
61612a7ff8fSkarels 				resid = ntohs(*(dmvdataaddr(dh, off+2, u_short *)));
61712a7ff8fSkarels 				if (off + resid > len)
61812a7ff8fSkarels 					goto setup;		/* sanity */
61912a7ff8fSkarels 				len = off + resid;
62012a7ff8fSkarels 			} else
62112a7ff8fSkarels 				off = 0;
62212a7ff8fSkarels 			if (len == 0)
62312a7ff8fSkarels 				goto setup;
62412a7ff8fSkarels 
62512a7ff8fSkarels 			/*
62612a7ff8fSkarels 			 * Pull packet off interface.  Off is nonzero if
62712a7ff8fSkarels 			 * packet has trailing header; dmv_get will then
62812a7ff8fSkarels 			 * force this header information to be at the front,
62912a7ff8fSkarels 			 * but we still have to drop the type and length
63012a7ff8fSkarels 			 * which are at the front of any trailer data.
63112a7ff8fSkarels 			 */
63212a7ff8fSkarels 			m = if_ubaget(&sc->sc_ifuba, ifrw, len, off, ifp);
63312a7ff8fSkarels 			if (m == 0)
63412a7ff8fSkarels 				goto setup;
63512a7ff8fSkarels 			switch (dh->dmv_type) {
63612a7ff8fSkarels #ifdef INET
63712a7ff8fSkarels 			case DMV_IPTYPE:
63812a7ff8fSkarels 				schednetisr(NETISR_IP);
63912a7ff8fSkarels 				inq = &ipintrq;
64012a7ff8fSkarels 				break;
64112a7ff8fSkarels #endif
64212a7ff8fSkarels 			default:
64312a7ff8fSkarels 				m_freem(m);
64412a7ff8fSkarels 				goto setup;
64512a7ff8fSkarels 			}
64612a7ff8fSkarels 
64712a7ff8fSkarels 			s = splimp();
64812a7ff8fSkarels 			if (IF_QFULL(inq)) {
64912a7ff8fSkarels 				IF_DROP(inq);
65012a7ff8fSkarels 				m_freem(m);
65112a7ff8fSkarels 			} else
65212a7ff8fSkarels 				IF_ENQUEUE(inq, m);
65312a7ff8fSkarels 			splx(s);
65412a7ff8fSkarels 	setup:
65512a7ff8fSkarels 			/* is this needed? */
656e578e9bdSkarels 			rp->ubinfo = UBAI_ADDR(ifrw->ifrw_info);
65712a7ff8fSkarels 			dmvload(
65812a7ff8fSkarels 				sc,
65912a7ff8fSkarels 				DMV_BACCR,
66012a7ff8fSkarels 				QP_SEL4|QP_SEL6|QP_SEL10,
66112a7ff8fSkarels 				0,
662e578e9bdSkarels 				(u_short) rp->ubinfo,
66312a7ff8fSkarels 				(rp->ubinfo>>16)&0x3f,
66412a7ff8fSkarels 				rp->cc
66512a7ff8fSkarels 			);
66612a7ff8fSkarels 			break;
66712a7ff8fSkarels 		case DMV_BDXSA:
66812a7ff8fSkarels 			/*
66912a7ff8fSkarels 			 * A write has completed, start another
67012a7ff8fSkarels 			 * transfer if there is more data to send.
67112a7ff8fSkarels 			 */
67212a7ff8fSkarels 			ifp->if_opackets++;
67312a7ff8fSkarels 			/* find associated dmvbuf structure */
67412a7ff8fSkarels 			ifxp = &sc->sc_ifw[0];
67512a7ff8fSkarels 			for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
67612a7ff8fSkarels 				if(rp->ubinfo == pkaddr)
67712a7ff8fSkarels 					break;
67812a7ff8fSkarels 				ifxp++;
67912a7ff8fSkarels 			}
68012a7ff8fSkarels 			if (rp >= &sc->sc_xbufs[NXMT]) {
68112a7ff8fSkarels 				log(LOG_ERR, "dmv%d: bad packet address 0x%x\n",
68212a7ff8fSkarels 				    unit, pkaddr);
68312a7ff8fSkarels 				break;
68412a7ff8fSkarels 			}
68512a7ff8fSkarels 			if ((rp->flags & DBUF_DMVS) == 0)
68612a7ff8fSkarels 				log(LOG_ERR, "dmvxint: dmv%d unallocated packet 0x%x\n",
68712a7ff8fSkarels 				    unit, pkaddr);
68812a7ff8fSkarels 			/* mark buffer free */
68912a7ff8fSkarels 			if (ifxp->ifw_xtofree) {
69012a7ff8fSkarels 				(void)m_freem(ifxp->ifw_xtofree);
69112a7ff8fSkarels 				ifxp->ifw_xtofree = 0;
69212a7ff8fSkarels 			}
69312a7ff8fSkarels 			rp->flags &= ~DBUF_DMVS;
694d468a3c3Skarels 			if (--sc->sc_oused == 0)
695d468a3c3Skarels 				sc->sc_if.if_timer = 0;
696d468a3c3Skarels 			else
697d468a3c3Skarels 				sc->sc_if.if_timer = dmv_timeout;
698d468a3c3Skarels 			if ((sc->sc_flag & DMV_ONLINE) == 0) {
699d468a3c3Skarels 				extern int ifqmaxlen;
700d468a3c3Skarels 
701d468a3c3Skarels 				/*
702d468a3c3Skarels 				 * We're on the air.
703d468a3c3Skarels 				 * Open the queue to the usual value.
704d468a3c3Skarels 				 */
705d468a3c3Skarels 				sc->sc_flag |= DMV_ONLINE;
706d468a3c3Skarels 				ifp->if_snd.ifq_maxlen = ifqmaxlen;
707d468a3c3Skarels 			}
70812a7ff8fSkarels 			break;
70912a7ff8fSkarels 
71012a7ff8fSkarels 		case DMV_CNTRLO:
71112a7ff8fSkarels 			/* ACCUMULATE STATISTICS */
71212a7ff8fSkarels 			switch(sel6&DMV_EEC) {
71312a7ff8fSkarels 			case DMV_ORUN:
71412a7ff8fSkarels 				if(sc->sc_flag & DMV_RESTART) {
71512a7ff8fSkarels 					load_rec_bufs(sc);
71612a7ff8fSkarels 					sc->sc_flag &= ~DMV_RESTART;
71712a7ff8fSkarels 					log(LOG_INFO,
718d468a3c3Skarels 					    "dmv%d: far end on-line\n", unit);
71912a7ff8fSkarels 				} else {
72012a7ff8fSkarels 					log(LOG_WARNING,
721d468a3c3Skarels 					    "dmv%d: far end restart\n", unit);
722d468a3c3Skarels 					goto restart;
72312a7ff8fSkarels 				}
72412a7ff8fSkarels 				break;
72512a7ff8fSkarels 			case DMV_RTE:
72612a7ff8fSkarels 				ifp->if_ierrors++;
72757dda7d5Skarels 				if ((sc->sc_rte++ % DMV_RPRTE) == 0)
72812a7ff8fSkarels 					log(LOG_WARNING,
729d468a3c3Skarels 				    "dmv%d: receive threshold error\n",
73057dda7d5Skarels 					    unit);
73112a7ff8fSkarels 				break;
73212a7ff8fSkarels 			case DMV_TTE:
73312a7ff8fSkarels 				ifp->if_oerrors++;
73457dda7d5Skarels 				if ((sc->sc_xte++ % DMV_RPTTE) == 0)
73512a7ff8fSkarels 					log(LOG_WARNING,
736d468a3c3Skarels 				    "dmv%d: transmit threshold error\n",
73757dda7d5Skarels 					    unit);
73812a7ff8fSkarels 				break;
73912a7ff8fSkarels 			case DMV_STE:
74057dda7d5Skarels 				if ((sc->sc_ste++ % DMV_RPSTE) == 0)
74112a7ff8fSkarels 					log(LOG_WARNING,
742d468a3c3Skarels 				    "dmv%d: select threshold error\n",
74357dda7d5Skarels 					    unit);
74412a7ff8fSkarels 				break;
74512a7ff8fSkarels 			case DMV_NXM:
74657dda7d5Skarels 				if ((sc->sc_nxm++ % DMV_RPNXM) == 0)
74712a7ff8fSkarels 					log(LOG_WARNING,
748d468a3c3Skarels 				    "dmv%d: nonexistent memory error\n",
74957dda7d5Skarels 					    unit);
75012a7ff8fSkarels 				break;
75112a7ff8fSkarels 			case DMV_MODD:
752d468a3c3Skarels 				if ((sc->sc_modd++ % DMV_RPMODD) == 0) {
75312a7ff8fSkarels 					log(LOG_WARNING,
754d468a3c3Skarels 				    "dmv%d: modem disconnected error\n",
75557dda7d5Skarels 					    unit);
756d468a3c3Skarels 					goto restart;
757d468a3c3Skarels 				}
75812a7ff8fSkarels 				break;
75912a7ff8fSkarels 			case DMV_CXRL:
76057dda7d5Skarels 				if ((sc->sc_cxrl++ % DMV_RPCXRL) == 0)
76112a7ff8fSkarels 					log(LOG_WARNING,
762d468a3c3Skarels 				    "dmv%d: carrier loss error\n",
76357dda7d5Skarels 					    unit);
76412a7ff8fSkarels 				break;
76512a7ff8fSkarels 			case DMV_QOVF:
76612a7ff8fSkarels 				log(LOG_WARNING,
767d468a3c3Skarels 				    "dmv%d: response queue overflow\n",
768d468a3c3Skarels 				    unit);
76912a7ff8fSkarels 				sc->sc_qovf++;
770d468a3c3Skarels 				goto restart;
77112a7ff8fSkarels 
77212a7ff8fSkarels 			default:
77312a7ff8fSkarels 				log(LOG_WARNING,
774d468a3c3Skarels 				    "dmv%d: unknown error %o\n",
775d468a3c3Skarels 				    unit, sel6&DMV_EEC);
77612a7ff8fSkarels 				if ((sc->sc_unknown++ % DMV_RPUNKNOWN) == 0)
777d468a3c3Skarels 					goto restart;
77812a7ff8fSkarels 				break;
77912a7ff8fSkarels 			}
78012a7ff8fSkarels 			break;
78112a7ff8fSkarels 
78212a7ff8fSkarels 		case DMV_BDRUNUS:
78312a7ff8fSkarels 		case DMV_BDXSN:
78412a7ff8fSkarels 		case DMV_BDXNS:
78512a7ff8fSkarels 			log(LOG_INFO,
786d468a3c3Skarels 			   "dmv%d: buffer disp for halted trib %o\n",
78712a7ff8fSkarels 			   unit, sel2&0x7
78812a7ff8fSkarels 		        );
78912a7ff8fSkarels 			break;
79012a7ff8fSkarels 
79112a7ff8fSkarels 		case DMV_MDEFO:
79212a7ff8fSkarels 			if((sel6&0x1f) == 020) {
79312a7ff8fSkarels 				log(LOG_INFO,
794d468a3c3Skarels 			   		"dmv%d: buffer return complete sel3=%x\n",
79512a7ff8fSkarels 			   		unit, sel3);
79612a7ff8fSkarels 			} else {
79712a7ff8fSkarels 				log(LOG_INFO,
798d468a3c3Skarels 			   	"dmv%d: info resp sel3=%x sel4=%x sel6=%x\n",
79912a7ff8fSkarels 			   	unit, sel3, sel4, sel6
80012a7ff8fSkarels 		        	);
80112a7ff8fSkarels 			}
80212a7ff8fSkarels 			break;
80312a7ff8fSkarels 
80412a7ff8fSkarels 		default:
805d468a3c3Skarels 			log(LOG_WARNING, "dmv%d: bad control %o\n",
80612a7ff8fSkarels 			   unit, sel2&0x7
80712a7ff8fSkarels 		        );
80812a7ff8fSkarels 			break;
80912a7ff8fSkarels 		}
81012a7ff8fSkarels 	}
81112a7ff8fSkarels 	dmvstart(unit);
81212a7ff8fSkarels 	return;
813d468a3c3Skarels restart:
81412a7ff8fSkarels 	dmvrestart(unit);
81512a7ff8fSkarels }
816d468a3c3Skarels 
load_rec_bufs(sc)81712a7ff8fSkarels load_rec_bufs(sc)
81812a7ff8fSkarels register struct dmv_softc *sc;
81912a7ff8fSkarels {
82012a7ff8fSkarels 	register struct dmvbufs *rp;
82112a7ff8fSkarels 
82212a7ff8fSkarels 	/* queue first NRCV buffers for DMV to fill */
82312a7ff8fSkarels 	for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
82412a7ff8fSkarels 		rp->flags |= DBUF_DMVS;
82512a7ff8fSkarels 		dmvload(
82612a7ff8fSkarels 			sc,
82712a7ff8fSkarels 			DMV_BACCR,
82812a7ff8fSkarels 			QP_TRIB|QP_SEL4|QP_SEL6|QP_SEL10,
82912a7ff8fSkarels 			1,
83012a7ff8fSkarels 			rp->ubinfo,
83112a7ff8fSkarels 			(rp->ubinfo>>16)&0x3f,
83212a7ff8fSkarels 			rp->cc
83312a7ff8fSkarels 		);
83412a7ff8fSkarels 		sc->sc_iused++;
83512a7ff8fSkarels 	}
83612a7ff8fSkarels }
83712a7ff8fSkarels 
83812a7ff8fSkarels /*
83912a7ff8fSkarels  * DMV output routine.
84012a7ff8fSkarels  * Encapsulate a packet of type family for the dmv.
84112a7ff8fSkarels  * Use trailer local net encapsulation if enough data in first
84212a7ff8fSkarels  * packet leaves a multiple of 512 bytes of data in remainder.
84312a7ff8fSkarels  */
dmvoutput(ifp,m0,dst)84412a7ff8fSkarels dmvoutput(ifp, m0, dst)
84512a7ff8fSkarels 	register struct ifnet *ifp;
84612a7ff8fSkarels 	register struct mbuf *m0;
84712a7ff8fSkarels 	struct sockaddr *dst;
84812a7ff8fSkarels {
84912a7ff8fSkarels 	int type, error, s;
85012a7ff8fSkarels 	register struct mbuf *m = m0;
85112a7ff8fSkarels 	register struct dmv_header *dh;
85212a7ff8fSkarels 	register int off;
85312a7ff8fSkarels 
854d468a3c3Skarels 	if ((ifp->if_flags & IFF_UP) == 0) {
855d468a3c3Skarels 		error = ENETDOWN;
856d468a3c3Skarels 		goto bad;
857d468a3c3Skarels 	}
858d468a3c3Skarels 
85912a7ff8fSkarels 	switch (dst->sa_family) {
86012a7ff8fSkarels #ifdef	INET
86112a7ff8fSkarels 	case AF_INET:
862e4e38973Ssklower 		off = m->m_pkthdr.len - m->m_len;
86312a7ff8fSkarels 		if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
86412a7ff8fSkarels 		if (off > 0 && (off & 0x1ff) == 0 &&
865e4e38973Ssklower 		    (m->m_flags & M_EXT) == 0 &&
866e4e38973Ssklower 		    m->m_data >= m->m_pktdat + 2 * sizeof (u_short)) {
86712a7ff8fSkarels 			type = DMV_TRAILER + (off>>9);
868e4e38973Ssklower 			m->m_data -= 2 * sizeof (u_short);
86912a7ff8fSkarels 			m->m_len += 2 * sizeof (u_short);
87012a7ff8fSkarels 			*mtod(m, u_short *) = htons((u_short)DMV_IPTYPE);
87112a7ff8fSkarels 			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
87212a7ff8fSkarels 			goto gottrailertype;
87312a7ff8fSkarels 		}
87412a7ff8fSkarels 		type = DMV_IPTYPE;
87512a7ff8fSkarels 		off = 0;
87612a7ff8fSkarels 		goto gottype;
87712a7ff8fSkarels #endif
87812a7ff8fSkarels 
87912a7ff8fSkarels 	case AF_UNSPEC:
88012a7ff8fSkarels 		dh = (struct dmv_header *)dst->sa_data;
88112a7ff8fSkarels 		type = dh->dmv_type;
88212a7ff8fSkarels 		goto gottype;
88312a7ff8fSkarels 
88412a7ff8fSkarels 	default:
885d468a3c3Skarels 		log(LOG_ERR, "dmvoutput, dmv%d can't handle af%d\n",
886d468a3c3Skarels 		    ifp->if_unit, dst->sa_family);
88712a7ff8fSkarels 		error = EAFNOSUPPORT;
88812a7ff8fSkarels 		goto bad;
88912a7ff8fSkarels 	}
89012a7ff8fSkarels 
89112a7ff8fSkarels gottrailertype:
89212a7ff8fSkarels 	/*
89312a7ff8fSkarels 	 * Packet to be sent as a trailer; move first packet
89412a7ff8fSkarels 	 * (control information) to end of chain.
89512a7ff8fSkarels 	 */
89612a7ff8fSkarels 	while (m->m_next)
89712a7ff8fSkarels 		m = m->m_next;
89812a7ff8fSkarels 	m->m_next = m0;
89912a7ff8fSkarels 	m = m0->m_next;
90012a7ff8fSkarels 	m0->m_next = 0;
90112a7ff8fSkarels 	m0 = m;
90212a7ff8fSkarels 
90312a7ff8fSkarels gottype:
90412a7ff8fSkarels 	/*
90512a7ff8fSkarels 	 * Add local network header
90612a7ff8fSkarels 	 * (there is space for a uba on a vax to step on)
90712a7ff8fSkarels 	 */
908e4e38973Ssklower 	M_PREPEND(m, sizeof(struct dmv_header), M_DONTWAIT);
90912a7ff8fSkarels 	if (m == 0) {
91012a7ff8fSkarels 		error = ENOBUFS;
91112a7ff8fSkarels 		goto bad;
91212a7ff8fSkarels 	}
91312a7ff8fSkarels 	dh = mtod(m, struct dmv_header *);
91412a7ff8fSkarels 	dh->dmv_type = htons((u_short)type);
91512a7ff8fSkarels 
91612a7ff8fSkarels 	/*
91712a7ff8fSkarels 	 * Queue message on interface, and start output if interface
91812a7ff8fSkarels 	 * not yet active.
91912a7ff8fSkarels 	 */
92012a7ff8fSkarels 	s = splimp();
92112a7ff8fSkarels 	if (IF_QFULL(&ifp->if_snd)) {
92212a7ff8fSkarels 		IF_DROP(&ifp->if_snd);
92312a7ff8fSkarels 		m_freem(m);
92412a7ff8fSkarels 		splx(s);
92512a7ff8fSkarels 		return (ENOBUFS);
92612a7ff8fSkarels 	}
92712a7ff8fSkarels 	IF_ENQUEUE(&ifp->if_snd, m);
92812a7ff8fSkarels 	dmvstart(ifp->if_unit);
92912a7ff8fSkarels 	splx(s);
93012a7ff8fSkarels 	return (0);
93112a7ff8fSkarels 
93212a7ff8fSkarels bad:
93312a7ff8fSkarels 	m_freem(m0);
93412a7ff8fSkarels 	return (error);
93512a7ff8fSkarels }
93612a7ff8fSkarels 
93712a7ff8fSkarels 
93812a7ff8fSkarels /*
93912a7ff8fSkarels  * Process an ioctl request.
94012a7ff8fSkarels  */
94112a7ff8fSkarels /* ARGSUSED */
dmvioctl(ifp,cmd,data)94212a7ff8fSkarels dmvioctl(ifp, cmd, data)
94312a7ff8fSkarels 	register struct ifnet *ifp;
94412a7ff8fSkarels 	int cmd;
94512a7ff8fSkarels 	caddr_t data;
94612a7ff8fSkarels {
94712a7ff8fSkarels 	int s = splimp(), error = 0;
94812a7ff8fSkarels 	struct mbuf *m;
94912a7ff8fSkarels 	register struct dmv_softc *sc = &dmv_softc[ifp->if_unit];
95012a7ff8fSkarels 
95112a7ff8fSkarels 	switch (cmd) {
95212a7ff8fSkarels 
95312a7ff8fSkarels 	case SIOCSIFADDR:
95412a7ff8fSkarels 		ifp->if_flags |= IFF_UP;
95512a7ff8fSkarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
95612a7ff8fSkarels 			dmvinit(ifp->if_unit);
95712a7ff8fSkarels 		break;
95812a7ff8fSkarels 
95912a7ff8fSkarels 	case SIOCSIFDSTADDR:
96012a7ff8fSkarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
96112a7ff8fSkarels 			dmvinit(ifp->if_unit);
96212a7ff8fSkarels 		break;
96312a7ff8fSkarels 
96412a7ff8fSkarels 	case SIOCSIFFLAGS:
96512a7ff8fSkarels 		if ((ifp->if_flags & IFF_UP) == 0 &&
966d468a3c3Skarels 		    sc->sc_flag & DMV_RUNNING)
967d468a3c3Skarels 			dmvdown(ifp->if_unit);
968d468a3c3Skarels 		else if (ifp->if_flags & IFF_UP &&
96912a7ff8fSkarels 		    (sc->sc_flag & DMV_RUNNING) == 0)
97012a7ff8fSkarels 			dmvrestart(ifp->if_unit);
97112a7ff8fSkarels 		break;
97212a7ff8fSkarels 
97312a7ff8fSkarels 	default:
97412a7ff8fSkarels 		error = EINVAL;
97512a7ff8fSkarels 	}
97612a7ff8fSkarels 	splx(s);
97712a7ff8fSkarels 	return (error);
97812a7ff8fSkarels }
97912a7ff8fSkarels 
98012a7ff8fSkarels /*
98112a7ff8fSkarels  * Restart after a fatal error.
98212a7ff8fSkarels  * Clear device and reinitialize.
98312a7ff8fSkarels  */
dmvrestart(unit)98412a7ff8fSkarels dmvrestart(unit)
98512a7ff8fSkarels 	int unit;
98612a7ff8fSkarels {
98712a7ff8fSkarels 	register struct dmvdevice *addr;
98812a7ff8fSkarels 	register int i;
98912a7ff8fSkarels 
990d468a3c3Skarels 	dmvdown(unit);
991d468a3c3Skarels 
992d468a3c3Skarels 	addr = (struct dmvdevice *)(dmvinfo[unit]->ui_addr);
99312a7ff8fSkarels 	/*
994d468a3c3Skarels 	 * Let the DMV finish the MCLR.
99512a7ff8fSkarels 	 */
99612a7ff8fSkarels 	for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--)
99712a7ff8fSkarels 		;
99812a7ff8fSkarels 	if ((addr->bsel1 & DMV_RUN) == 0) {
99912a7ff8fSkarels 		log(LOG_ERR, "dmvrestart: can't start device\n" );
100012a7ff8fSkarels 		return (0);
100112a7ff8fSkarels 	}
100212a7ff8fSkarels 	if ((addr->bsel4 != 033) || (addr->bsel6 != 0305))
100312a7ff8fSkarels 	{
1004d468a3c3Skarels 		log(LOG_ERR, "dmv%d: device init failed, bsel4=%o, bsel6=%o\n",
1005d468a3c3Skarels 			unit, addr->bsel4, addr->bsel6);
100612a7ff8fSkarels 		return (0);
100712a7ff8fSkarels 	}
1008d468a3c3Skarels 
1009d468a3c3Skarels 	/* restart DMV */
1010d468a3c3Skarels 	dmvinit(unit);
1011d468a3c3Skarels 	dmv_softc[unit].sc_if.if_collisions++;	/* why not? */
1012d468a3c3Skarels }
1013d468a3c3Skarels 
1014d468a3c3Skarels /*
1015d468a3c3Skarels  * Reset a device and mark down.
1016d468a3c3Skarels  * Flush output queue and drop queue limit.
1017d468a3c3Skarels  */
dmvdown(unit)1018d468a3c3Skarels dmvdown(unit)
1019d468a3c3Skarels 	int unit;
1020d468a3c3Skarels {
1021d468a3c3Skarels 	struct dmv_softc *sc = &dmv_softc[unit];
1022d468a3c3Skarels 	register struct ifxmt *ifxp;
1023d468a3c3Skarels 
1024d468a3c3Skarels 	((struct dmvdevice *)(dmvinfo[unit]->ui_addr))->bsel1 = DMV_MCLR;
1025d468a3c3Skarels 	sc->sc_flag &= ~(DMV_RUNNING | DMV_ONLINE);
1026d468a3c3Skarels 
102712a7ff8fSkarels 	for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) {
102812a7ff8fSkarels 		if (ifxp->ifw_xtofree) {
102912a7ff8fSkarels 			(void) m_freem(ifxp->ifw_xtofree);
103012a7ff8fSkarels 			ifxp->ifw_xtofree = 0;
103112a7ff8fSkarels 		}
103212a7ff8fSkarels 	}
1033d468a3c3Skarels 	sc->sc_oused = 0;
1034d468a3c3Skarels 	if_qflush(&sc->sc_if.if_snd);
1035d468a3c3Skarels 
1036d468a3c3Skarels 	/*
1037d468a3c3Skarels 	 * Limit packets enqueued until we're back on the air.
1038d468a3c3Skarels 	 */
1039d468a3c3Skarels 	sc->sc_if.if_snd.ifq_maxlen = 3;
104012a7ff8fSkarels }
104112a7ff8fSkarels 
104212a7ff8fSkarels /*
1043d468a3c3Skarels  * Watchdog timeout to see that transmitted packets don't
1044d468a3c3Skarels  * lose interrupts.  The device has to be online.
104512a7ff8fSkarels  */
dmvtimeout(unit)1046d468a3c3Skarels dmvtimeout(unit)
1047d468a3c3Skarels 	int unit;
104812a7ff8fSkarels {
104912a7ff8fSkarels 	register struct dmv_softc *sc;
105012a7ff8fSkarels 	struct dmvdevice *addr;
105112a7ff8fSkarels 
1052d468a3c3Skarels 	sc = &dmv_softc[unit];
1053d468a3c3Skarels 	if (sc->sc_flag & DMV_ONLINE) {
1054d468a3c3Skarels 		addr = (struct dmvdevice *)(dmvinfo[unit]->ui_addr);
1055d468a3c3Skarels 		log(LOG_ERR, "dmv%d: output timeout, bsel0=%b bsel2=%b\n",
1056d468a3c3Skarels 		    unit, addr->bsel0 & 0xff, DMV0BITS,
105712a7ff8fSkarels 		    addr->bsel2 & 0xff, DMV2BITS);
1058d468a3c3Skarels 		dmvrestart(unit);
105912a7ff8fSkarels 	}
106012a7ff8fSkarels }
106112a7ff8fSkarels #endif
1062