xref: /csrg-svn/sys/vax/if/if_dmv.c (revision 36032)
131908Skarels /*
234727Smckusick  * Copyright (c) 1988 Regents of the University of California.
334727Smckusick  * All rights reserved.
434727Smckusick  *
534727Smckusick  * Redistribution and use in source and binary forms are permitted
634868Sbostic  * provided that the above copyright notice and this paragraph are
734868Sbostic  * duplicated in all such forms and that any documentation,
834868Sbostic  * advertising materials, and other materials related to such
934868Sbostic  * distribution and use acknowledge that the software was developed
1034868Sbostic  * by the University of California, Berkeley.  The name of the
1134868Sbostic  * University may not be used to endorse or promote products derived
1234868Sbostic  * from this software without specific prior written permission.
1334868Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434868Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534868Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1634727Smckusick  *
17*36032Skarels  *	@(#)if_dmv.c	7.7 (Berkeley) 10/22/88
1834868Sbostic  */
1934868Sbostic 
2034868Sbostic /*
2131908Skarels  * DMV-11 Driver
2231908Skarels  *
2331908Skarels  * Qbus Sync DDCMP interface - DMV operated in full duplex, point to point mode
2431908Skarels  *
2534727Smckusick  * Written by Bob Kridle of Mt Xinu
2634727Smckusick  * starting from if_dmc.c version 6.12 dated 4/23/86
2731908Skarels  */
2831908Skarels 
2931908Skarels #include "dmv.h"
3031908Skarels #if NDMV > 0
3131908Skarels 
3231908Skarels #include "param.h"
3331908Skarels #include "systm.h"
3431908Skarels #include "mbuf.h"
3531908Skarels #include "buf.h"
3631908Skarels #include "ioctl.h"		/* must precede tty.h */
3731908Skarels #include "tty.h"
3831908Skarels #include "protosw.h"
3931908Skarels #include "socket.h"
4031908Skarels #include "syslog.h"
4131908Skarels #include "vmmac.h"
4231908Skarels #include "errno.h"
4334529Skarels #include "time.h"
4434529Skarels #include "kernel.h"
4531908Skarels 
4631908Skarels #include "../net/if.h"
4731908Skarels #include "../net/netisr.h"
4831908Skarels #include "../net/route.h"
4931908Skarels 
5031908Skarels #ifdef	INET
5131908Skarels #include "../netinet/in.h"
5231908Skarels #include "../netinet/in_systm.h"
5331908Skarels #include "../netinet/in_var.h"
5431908Skarels #include "../netinet/ip.h"
5531908Skarels #endif
5631908Skarels 
5731908Skarels #include "../vax/cpu.h"
5831908Skarels #include "../vax/mtpr.h"
5934529Skarels #include "../vax/pte.h"
6034529Skarels #include "../vaxuba/ubareg.h"
6134529Skarels #include "../vaxuba/ubavar.h"
6231908Skarels #include "if_uba.h"
6331908Skarels #include "if_dmv.h"
6431908Skarels 
6531908Skarels int	dmv_timeout = 8;		/* timeout value */
6631908Skarels 
6731908Skarels /*
6831908Skarels  * Driver information for auto-configuration stuff.
6931908Skarels  */
7031908Skarels int	dmvprobe(), dmvattach(), dmvinit(), dmvioctl();
7134529Skarels int	dmvoutput(), dmvreset(), dmvtimeout();
7231908Skarels struct	uba_device *dmvinfo[NDMV];
7331908Skarels u_short	dmvstd[] = { 0 };
7431908Skarels struct	uba_driver dmvdriver =
7531908Skarels 	{ dmvprobe, 0, dmvattach, 0, dmvstd, "dmv", dmvinfo };
7631908Skarels 
7731908Skarels /*
7831908Skarels  * Don't really know how many buffers/commands can be queued to a DMV-11.
7931908Skarels  * Manual doesn't say... Perhaps we can look at a DEC driver some day.
8034529Skarels  * These numbers ame from DMC/DMR driver.
8131908Skarels  */
8231908Skarels #define NRCV 5
8331908Skarels #define NXMT 3
8431908Skarels #define NCMDS	(NRCV+NXMT+4)	/* size of command queue */
8531908Skarels 
8634529Skarels #ifdef DEBUG
8734529Skarels #define printd(f)   if (sc->sc_if.if_flags & IFF_DEBUG) \
8834529Skarels 	printf("DMVDEBUG: dmv%d: ", unit), printf(f)
8934529Skarels #else
9034529Skarels #define	printd(f)	/* nil */
9134529Skarels #endif
9231908Skarels 
9331908Skarels /* error reporting intervals */
9431908Skarels 
9531908Skarels #define	DMV_RPRTE	 1
9631908Skarels #define	DMV_RPTTE        1
9731908Skarels #define	DMV_RPSTE	 1
9831908Skarels #define DMV_RPNXM        1
9931908Skarels #define DMV_RPMODD       1
10031908Skarels #define DMV_RPQOVF	 1
10131908Skarels #define DMV_RPCXRL	 1
10231908Skarels 
10331909Skarels /* number of errors to accept before trying a reset */
10431909Skarels #define DMV_RPUNKNOWN	 10
10531909Skarels 
10631908Skarels struct  dmv_command {
10731908Skarels 	u_char	qp_mask;	/* Which registers to set up */
10831908Skarels #define	QP_TRIB		0x01
10931908Skarels #define	QP_SEL4		0x02
11031908Skarels #define	QP_SEL6		0x04
11131908Skarels #define	QP_SEL10	0x08
11231908Skarels 	u_char	qp_cmd;
11331908Skarels 	u_char	qp_tributary;
11431908Skarels 	u_short	qp_sel4;
11531908Skarels 	u_short	qp_sel6;
11631908Skarels 	u_short	qp_sel10;
11731908Skarels 	struct	dmv_command *qp_next;	/* next command on queue */
11831908Skarels };
11931908Skarels 
12031908Skarels #define	qp_lowbufaddr	qp_
12131908Skarels 
12231908Skarels struct dmvbufs {
12331908Skarels 	int	ubinfo;		/* from uballoc */
12431908Skarels 	short	cc;		/* buffer size */
12531908Skarels 	short	flags;		/* access control */
12631908Skarels };
12731908Skarels 
12831908Skarels #define	DBUF_OURS	0	/* buffer is available */
12931908Skarels #define	DBUF_DMVS	1	/* buffer claimed by somebody */
13031908Skarels #define	DBUF_XMIT	4	/* transmit buffer */
13131908Skarels #define	DBUF_RCV	8	/* receive buffer */
13231908Skarels 
13331908Skarels 
13431908Skarels /*
13531908Skarels  * DMV software status per interface.
13631908Skarels  *
13731908Skarels  * Each interface is referenced by a network interface structure,
13831908Skarels  * sc_if, which the routing code uses to locate the interface.
13931908Skarels  * This structure contains the output queue for the interface, its address, ...
14031908Skarels  * We also have, for each interface, a  set of 7 UBA interface structures
14131908Skarels  * for each, which
14231908Skarels  * contain information about the UNIBUS resources held by the interface:
14331908Skarels  * map registers, buffered data paths, etc.  Information is cached in this
14431908Skarels  * structure for use by the if_uba.c routines in running the interface
14531908Skarels  * efficiently.
14631908Skarels  */
14731908Skarels struct dmv_softc {
14831908Skarels 	struct	ifnet sc_if;		/* network-visible interface */
14931908Skarels 	short	sc_oused;		/* output buffers currently in use */
15031908Skarels 	short	sc_iused;		/* input buffers given to DMV */
15131908Skarels 	short	sc_flag;		/* flags */
152*36032Skarels 	short	sc_ipl;			/* interrupt priority */
15331908Skarels 	int	sc_ubinfo;		/* UBA mapping info for base table */
15431908Skarels 	int	sc_errors[8];		/* error counters */
15531908Skarels #define	sc_rte	sc_errors[0]		/* receive threshhold error */
15631908Skarels #define	sc_xte	sc_errors[1]		/* xmit threshhold error */
15731908Skarels #define	sc_ste	sc_errors[2]		/* select threshhold error */
15831908Skarels #define	sc_nxm	sc_errors[3]		/* non-existant memory */
15931908Skarels #define	sc_modd	sc_errors[4]		/* modem disconnect */
16031908Skarels #define	sc_qovf	sc_errors[5]		/* command/response queue overflow */
16131908Skarels #define	sc_cxrl	sc_errors[6]		/* carrier loss */
16231908Skarels #define sc_unknown sc_errors[7]		/* other errors - look in DMV manual */
16334529Skarels 	struct	dmvbufs sc_rbufs[NRCV];	/* receive buffer info */
16434529Skarels 	struct	dmvbufs sc_xbufs[NXMT];	/* transmit buffer info */
16534529Skarels 	struct	ifubinfo sc_ifuba;	/* UNIBUS resources */
16634529Skarels 	struct	ifrw sc_ifr[NRCV];	/* UNIBUS receive buffer maps */
16734529Skarels 	struct	ifxmt sc_ifw[NXMT];	/* UNIBUS receive buffer maps */
16831908Skarels 	/* command queue stuff */
16931908Skarels 	struct	dmv_command sc_cmdbuf[NCMDS];
17031908Skarels 	struct	dmv_command *sc_qhead;	/* head of command queue */
17131908Skarels 	struct	dmv_command *sc_qtail;	/* tail of command queue */
17231908Skarels 	struct	dmv_command *sc_qactive;	/* command in progress */
17331908Skarels 	struct	dmv_command *sc_qfreeh;	/* head of list of free cmd buffers */
17431908Skarels 	struct	dmv_command *sc_qfreet;	/* tail of list of free cmd buffers */
17531908Skarels 	/* end command queue stuff */
17631908Skarels } dmv_softc[NDMV];
17731908Skarels 
17831908Skarels /* flags */
17934529Skarels #define DMV_RESTART	0x01		/* software restart in progress */
18034529Skarels #define DMV_ONLINE	0x02		/* device managed to transmit */
18134529Skarels #define DMV_RUNNING	0x04		/* device initialized */
18231908Skarels 
18331908Skarels 
18431908Skarels /* queue manipulation macros */
18531908Skarels #define	QUEUE_AT_HEAD(qp, head, tail) \
18631908Skarels 	(qp)->qp_next = (head); \
18731908Skarels 	(head) = (qp); \
18831908Skarels 	if ((tail) == (struct dmv_command *) 0) \
18931908Skarels 		(tail) = (head)
19031908Skarels 
19131908Skarels #define QUEUE_AT_TAIL(qp, head, tail) \
19231908Skarels 	if ((tail)) \
19331908Skarels 		(tail)->qp_next = (qp); \
19431908Skarels 	else \
19531908Skarels 		(head) = (qp); \
19631908Skarels 	(qp)->qp_next = (struct dmv_command *) 0; \
19731908Skarels 	(tail) = (qp)
19831908Skarels 
19931908Skarels #define DEQUEUE(head, tail) \
20031908Skarels 	(head) = (head)->qp_next;\
20131908Skarels 	if ((head) == (struct dmv_command *) 0)\
20231908Skarels 		(tail) = (head)
20331908Skarels 
204*36032Skarels dmvprobe(reg, ui)
20531908Skarels 	caddr_t reg;
206*36032Skarels 	struct uba_device *ui;
20731908Skarels {
20831908Skarels 	register int br, cvec;
20931908Skarels 	register struct dmvdevice *addr = (struct dmvdevice *)reg;
21031908Skarels 	register int i;
21131908Skarels 
21231908Skarels #ifdef lint
21331908Skarels 	br = 0; cvec = br; br = cvec;
21431908Skarels 	dmvrint(0); dmvxint(0);
21531908Skarels #endif
21631908Skarels 	addr->bsel1 = DMV_MCLR;
21731908Skarels 	for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--)
21831908Skarels 		;
21931908Skarels 	if ((addr->bsel1 & DMV_RUN) == 0) {
22031908Skarels 		printf("dmvprobe: can't start device\n" );
22131908Skarels 		return (0);
22231908Skarels 	}
22331908Skarels 	if ((addr->bsel4 != 033) || (addr->bsel6 != 0305))
22431908Skarels 	{
22531908Skarels 		printf("dmvprobe: device init failed, bsel4=%o, bsel6=%o\n",
22631908Skarels 			addr->bsel4, addr->bsel6);
22731908Skarels 		return (0);
22831908Skarels 	}
229*36032Skarels 	(void) spl6();
23031908Skarels 	addr->bsel0 = DMV_RQI|DMV_IEI|DMV_IEO;
23131908Skarels 	DELAY(1000000);
23231908Skarels 	addr->bsel1 = DMV_MCLR;
23331908Skarels 	for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--)
23431908Skarels 		;
235*36032Skarels 	dmv_softc[ui->ui_unit].sc_ipl = br = qbgetpri();
236*36032Skarels 	return (sizeof(struct dmvdevice));
23731908Skarels }
23831908Skarels 
23931908Skarels /*
24031908Skarels  * Interface exists: make available by filling in network interface
24131908Skarels  * record.  System will initialize the interface when it is ready
24231908Skarels  * to accept packets.
24331908Skarels  */
24431908Skarels dmvattach(ui)
24531908Skarels 	register struct uba_device *ui;
24631908Skarels {
24731908Skarels 	register struct dmv_softc *sc = &dmv_softc[ui->ui_unit];
24831908Skarels 
24931908Skarels 	sc->sc_if.if_unit = ui->ui_unit;
25031908Skarels 	sc->sc_if.if_name = "dmv";
25131908Skarels 	sc->sc_if.if_mtu = DMVMTU;
25231908Skarels 	sc->sc_if.if_init = dmvinit;
25331908Skarels 	sc->sc_if.if_output = dmvoutput;
25431908Skarels 	sc->sc_if.if_ioctl = dmvioctl;
25531908Skarels 	sc->sc_if.if_reset = dmvreset;
25634529Skarels 	sc->sc_if.if_watchdog = dmvtimeout;
25731908Skarels 	sc->sc_if.if_flags = IFF_POINTOPOINT;
25831908Skarels 	sc->sc_ifuba.iff_flags = UBA_CANTWAIT;
25931908Skarels 
26031908Skarels 	if_attach(&sc->sc_if);
26131908Skarels }
26231908Skarels 
26331908Skarels /*
26431908Skarels  * Reset of interface after UNIBUS reset.
26531908Skarels  * If interface is on specified UBA, reset its state.
26631908Skarels  */
26731908Skarels dmvreset(unit, uban)
26831908Skarels 	int unit, uban;
26931908Skarels {
27031908Skarels 	register struct uba_device *ui;
27131908Skarels 	register struct dmv_softc *sc = &dmv_softc[unit];
27231908Skarels 
27331908Skarels 	if (unit >= NDMV || (ui = dmvinfo[unit]) == 0 || ui->ui_alive == 0 ||
27431908Skarels 	    ui->ui_ubanum != uban)
27531908Skarels 		return;
27631908Skarels 	printf(" dmv%d", unit);
27731908Skarels 	sc->sc_flag = 0;
27831908Skarels 	sc->sc_if.if_flags &= ~IFF_RUNNING;
27931908Skarels 	dmvinit(unit);
28031908Skarels }
28131908Skarels 
28231908Skarels /*
28331908Skarels  * Initialization of interface; reinitialize UNIBUS usage.
28431908Skarels  */
28531908Skarels dmvinit(unit)
28631908Skarels 	int unit;
28731908Skarels {
28831908Skarels 	register struct dmv_softc *sc = &dmv_softc[unit];
28931908Skarels 	register struct uba_device *ui = dmvinfo[unit];
29031908Skarels 	register struct dmvdevice *addr;
29131908Skarels 	register struct ifnet *ifp = &sc->sc_if;
29231908Skarels 	register struct ifrw *ifrw;
29331908Skarels 	register struct ifxmt *ifxp;
29431908Skarels 	register struct dmvbufs *rp;
29531908Skarels 	register struct dmv_command *qp;
29631908Skarels 	struct ifaddr *ifa;
29731908Skarels 	int base;
29831908Skarels 	int s;
29931908Skarels 
30031908Skarels 	addr = (struct dmvdevice *)ui->ui_addr;
30131908Skarels 
30231908Skarels 	/*
30331908Skarels 	 * Check to see that an address has been set
30431908Skarels 	 * (both local and destination for an address family).
30531908Skarels 	 */
30631908Skarels 	for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
30731908Skarels 		if (ifa->ifa_addr.sa_family && ifa->ifa_dstaddr.sa_family)
30831908Skarels 			break;
30931908Skarels 	if (ifa == (struct ifaddr *) 0)
31031908Skarels 		return;
31131908Skarels 
31231908Skarels 	if ((addr->bsel1&DMV_RUN) == 0) {
31331908Skarels 		log(LOG_CRIT, "dmvinit: dmv%d not running\n", unit);
31431908Skarels 		ifp->if_flags &= ~IFF_UP;
31531908Skarels 		return;
31631908Skarels 	}
31734529Skarels 	printd(("dmvinit\n"));
31831908Skarels 	/* initialize UNIBUS resources */
31931908Skarels 	sc->sc_iused = sc->sc_oused = 0;
32031908Skarels 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
32131908Skarels 		if (if_ubaminit(
32231908Skarels 			&sc->sc_ifuba,
32331908Skarels 			ui->ui_ubanum,
32431908Skarels 		    	sizeof(struct dmv_header),
32531908Skarels 			(int)btoc(DMVMTU),
32631908Skarels 			sc->sc_ifr,
32731908Skarels 			NRCV,
32831908Skarels 			sc->sc_ifw,
32931908Skarels 			NXMT
33031908Skarels 	      	) == 0) {
33131908Skarels 			log(LOG_CRIT, "dmvinit: dmv%d can't allocate uba resources\n", unit);
33231908Skarels 			ifp->if_flags &= ~IFF_UP;
33331908Skarels 			return;
33431908Skarels 		}
33531908Skarels 		ifp->if_flags |= IFF_RUNNING;
33631908Skarels 	}
33734529Skarels 	/*
33834529Skarels 	 * Limit packets enqueued until we see if we're on the air.
33934529Skarels 	 */
34034529Skarels 	ifp->if_snd.ifq_maxlen = 3;
34131908Skarels 
34234529Skarels 
34331908Skarels 	/* initialize buffer pool */
34431908Skarels 	/* receives */
34531908Skarels 	ifrw = &sc->sc_ifr[0];
34631908Skarels 	for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
347*36032Skarels 		rp->ubinfo = UBAI_ADDR(ifrw->ifrw_info);
34831908Skarels 		rp->cc = DMVMTU + sizeof (struct dmv_header);
34931908Skarels 		rp->flags = DBUF_OURS|DBUF_RCV;
35031908Skarels 		ifrw++;
35131908Skarels 	}
35231908Skarels 	/* transmits */
35331908Skarels 	ifxp = &sc->sc_ifw[0];
35431908Skarels 	for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
355*36032Skarels 		rp->ubinfo = UBAI_ADDR(ifxp->ifw_info);
35631908Skarels 		rp->cc = 0;
35731908Skarels 		rp->flags = DBUF_OURS|DBUF_XMIT;
35831908Skarels 		ifxp++;
35931908Skarels 	}
36031908Skarels 
36131908Skarels 	/* set up command queues */
36231908Skarels 	sc->sc_qfreeh = sc->sc_qfreet
36331908Skarels 		 = sc->sc_qhead = sc->sc_qtail = sc->sc_qactive =
36431908Skarels 		(struct dmv_command *)0;
36531908Skarels 	/* set up free command buffer list */
36631908Skarels 	for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) {
36731908Skarels 		QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
36831908Skarels 	}
36931908Skarels 	if(sc->sc_flag & DMV_RUNNING)
37031908Skarels 		dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_REQHS,0);
37131908Skarels 	else
37231908Skarels 		dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_ESTTRIB,0);
37331908Skarels 	dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_REQSUS,0);
37431908Skarels 	sc->sc_flag |= (DMV_RESTART|DMV_RUNNING);
37534529Skarels 	sc->sc_flag &= ~DMV_ONLINE;
37631908Skarels 	addr->bsel0 |= DMV_IEO;
37731908Skarels }
37831908Skarels 
37931908Skarels /*
38031908Skarels  * Start output on interface.  Get another datagram
38131908Skarels  * to send from the interface queue and map it to
38231908Skarels  * the interface before starting output.
38331908Skarels  *
38431908Skarels  * Must be called at spl 5
38531908Skarels  */
38631908Skarels dmvstart(dev)
38731908Skarels 	dev_t dev;
38831908Skarels {
38931908Skarels 	int unit = minor(dev);
39031908Skarels 	register struct dmv_softc *sc = &dmv_softc[unit];
39131908Skarels 	struct mbuf *m;
39231908Skarels 	register struct dmvbufs *rp;
39331908Skarels 	register int n;
39431908Skarels 
39531908Skarels 	/*
39631908Skarels 	 * Dequeue up to NXMT requests and map them to the UNIBUS.
39731908Skarels 	 * If no more requests, or no dmv buffers available, just return.
39831908Skarels 	 */
39934529Skarels 	printd(("dmvstart\n"));
40031908Skarels 	n = 0;
40131908Skarels 	for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) {
40231908Skarels 		/* find an available buffer */
40331908Skarels 		if ((rp->flags & DBUF_DMVS) == 0) {
40431908Skarels 			IF_DEQUEUE(&sc->sc_if.if_snd, m);
40531908Skarels 			if (m == 0)
40631908Skarels 				return;
40731908Skarels 			/* mark it dmvs */
40831908Skarels 			rp->flags |= (DBUF_DMVS);
40931908Skarels 			/*
41031908Skarels 			 * Have request mapped to UNIBUS for transmission
41131908Skarels 			 * and start the output.
41231908Skarels 			 */
41331908Skarels 			rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m);
41434529Skarels 			if (++sc->sc_oused == 1)
41534529Skarels 				sc->sc_if.if_timer = dmv_timeout;
41631908Skarels 			dmvload(
41731908Skarels 				sc,
41831908Skarels 				DMV_BACCX,
41931908Skarels 				QP_TRIB|QP_SEL4|QP_SEL6|QP_SEL10,
42031908Skarels 				1,
42131908Skarels 				rp->ubinfo,
42231908Skarels 				(rp->ubinfo>>16)&0x3f,
42331908Skarels 				rp->cc
42431908Skarels 			);
42531908Skarels 		}
42631908Skarels 		n++;
42731908Skarels 	}
42831908Skarels }
42931908Skarels 
43031908Skarels /*
43131908Skarels  * Utility routine to load the DMV device registers.
43231908Skarels  */
43331908Skarels dmvload(sc, cmd, mask, tributary, sel4, sel6, sel10)
43431908Skarels 	register struct dmv_softc *sc;
43531908Skarels 	u_char cmd, tributary, mask;
43631908Skarels 	u_short sel4, sel6, sel10;
43731908Skarels {
43831908Skarels 	register struct dmvdevice *addr;
43931908Skarels 	register int unit, sps;
44031908Skarels 	register struct dmv_command *qp;
44131908Skarels 
44231908Skarels 	unit = sc - dmv_softc;
44334529Skarels 	printd(("dmvload: cmd=%x mask=%x trib=%x sel4=%x sel6=%x sel10=%x\n",
44431908Skarels 		(unsigned) cmd,
44531908Skarels 		(unsigned) mask,
44631908Skarels 		(unsigned) tributary,
44731908Skarels 		(unsigned) sel4,
44831908Skarels 		(unsigned) sel6,
44931908Skarels 		(unsigned) sel10
45034529Skarels 	));
45131908Skarels 	addr = (struct dmvdevice *)dmvinfo[unit]->ui_addr;
45231908Skarels 	sps = spl5();
45331908Skarels 
45431908Skarels 	/* grab a command buffer from the free list */
45531908Skarels 	if ((qp = sc->sc_qfreeh) == (struct dmv_command *)0)
45631908Skarels 		panic("dmv command queue overflow");
45731908Skarels 	DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet);
45831908Skarels 
45931908Skarels 	/* fill in requested info */
46031908Skarels 	qp->qp_cmd = cmd;
46131908Skarels 	qp->qp_mask = mask;
46231908Skarels 	qp->qp_tributary = tributary;
46331908Skarels 	qp->qp_sel4 = sel4;
46431908Skarels 	qp->qp_sel6 = sel6;
46531908Skarels 	qp->qp_sel10 = sel10;
46631908Skarels 
46731908Skarels 	if (sc->sc_qactive) {	/* command in progress */
46831908Skarels 		if (cmd == DMV_BACCR) {  /* supply read buffers first */
46931908Skarels 			QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail);
47031908Skarels 		} else {
47131908Skarels 			QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail);
47231908Skarels 		}
47331908Skarels 	} else {	/* command port free */
47431908Skarels 		sc->sc_qactive = qp;
47531908Skarels 		addr->bsel0 = (DMV_RQI|DMV_IEI|DMV_IEO);
47631908Skarels 	}
47731908Skarels 	splx(sps);
47831908Skarels }
47931908Skarels /*
48031908Skarels  * DMV interface input interrupt.
48131908Skarels  * Ready to accept another command,
48231908Skarels  * pull one off the command queue.
48331908Skarels  */
48431908Skarels dmvrint(unit)
48531908Skarels 	int unit;
48631908Skarels {
48731908Skarels 	register struct dmv_softc *sc;
48831908Skarels 	register struct dmvdevice *addr;
48931908Skarels 	register struct dmv_command *qp;
49031908Skarels 	register int n;
49131908Skarels 
49231908Skarels 	addr = (struct dmvdevice *)dmvinfo[unit]->ui_addr;
49331908Skarels 	sc = &dmv_softc[unit];
494*36032Skarels 	splx(sc->sc_ipl);
49534529Skarels 	printd(("dmvrint\n"));
49631908Skarels 	if ((qp = sc->sc_qactive) == (struct dmv_command *) 0) {
49731908Skarels 		log(LOG_WARNING, "dmvrint: dmv%d no command\n", unit);
49831908Skarels 		return;
49931908Skarels 	}
50031908Skarels 	while (addr->bsel2&DMV_RDI) {
50131908Skarels 		if(qp->qp_mask&QP_SEL4)
50231908Skarels 			addr->wsel4 = qp->qp_sel4;
50331908Skarels 		if(qp->qp_mask&QP_SEL6)
50431908Skarels 			addr->wsel6 = qp->qp_sel6;
50531908Skarels 		if(qp->qp_mask&QP_SEL10) {
50631908Skarels 			addr->wsel10 = qp->qp_sel10;
50731908Skarels 			qp->qp_cmd |= DMV_22BIT;
50831908Skarels 		}
50931908Skarels 		if(qp->qp_mask&QP_TRIB)
51031908Skarels 			addr->wsel2 = qp->qp_cmd|(qp->qp_tributary << 8);
51131908Skarels 		else
51231908Skarels 			addr->bsel2 = qp->qp_cmd;
51331908Skarels 		QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
51431908Skarels 		if ((sc->sc_qactive = sc->sc_qhead) == (struct dmv_command *)0)
51531908Skarels 			break;
51631908Skarels 		qp = sc->sc_qactive;
51731908Skarels 		DEQUEUE(sc->sc_qhead, sc->sc_qtail);
51831908Skarels 		if (addr->bsel2&DMV_RDO)
51931908Skarels 				break;
52031908Skarels 	}
52131908Skarels 	if (!sc->sc_qactive) {
52231908Skarels 		if(addr->bsel2&DMV_RDI) {
52331908Skarels 			/* clear RQI prior to last command per DMV manual */
52431908Skarels 			addr->bsel0 &= ~DMV_RQI;
52531908Skarels 			addr->wsel6 = DMV_NOP;
52631908Skarels 			addr->bsel2 = DMV_CNTRLI;
52731908Skarels 		}
52831908Skarels 		addr->bsel0 = DMV_IEO;
52931908Skarels 	}
53031908Skarels 	else /* RDO set or DMV still holding CSR */
53131908Skarels 		addr->bsel0 = (DMV_RQI|DMV_IEI|DMV_IEO);
53231908Skarels 
53331908Skarels }
53431908Skarels 
53531908Skarels /*
53631908Skarels  * DMV interface output interrupt.
53731908Skarels  * A transfer may have completed, check for errors.
53831908Skarels  * If it was a read, notify appropriate protocol.
53931908Skarels  * If it was a write, pull the next one off the queue.
54031908Skarels  */
54131908Skarels dmvxint(unit)
54231908Skarels 	int unit;
54331908Skarels {
54431908Skarels 	register struct dmv_softc *sc;
54531908Skarels 	register struct ifnet *ifp;
54631908Skarels 	struct uba_device *ui = dmvinfo[unit];
54731908Skarels 	struct dmvdevice *addr;
54831908Skarels 	struct mbuf *m;
54931908Skarels 	struct ifqueue *inq;
55031908Skarels 	int sel2, sel3, sel4, sel6, sel10, pkaddr, len, s;
55131908Skarels 	register struct ifrw *ifrw;
55231908Skarels 	register struct dmvbufs *rp;
55331908Skarels 	register struct ifxmt *ifxp;
55431908Skarels 	struct dmv_header *dh;
55534529Skarels 	int off, resid;
55631908Skarels 
55731908Skarels 	addr = (struct dmvdevice *)ui->ui_addr;
55831908Skarels 	sc = &dmv_softc[unit];
559*36032Skarels 	splx(sc->sc_ipl);
56031908Skarels 	ifp = &sc->sc_if;
56131908Skarels 
56231908Skarels 	while (addr->bsel2 & DMV_RDO) {
56331908Skarels 
56431908Skarels 		sel2 = addr->bsel2;
56531908Skarels 		sel3 = addr->bsel3;
56631908Skarels 		sel4 = addr->wsel4;		/* release port */
56731908Skarels 		sel6 = addr->wsel6;
56831908Skarels 		if(sel2 & DMV_22BIT)
56931908Skarels 			sel10 = addr->wsel10;
57031908Skarels 		addr->bsel2 &= ~DMV_RDO;
57131908Skarels 		pkaddr =  sel4 | ((sel6 & 0x3f) << 16);
57234529Skarels 		printd(("dmvxint: sel2=%x sel4=%x sel6=%x sel10=%x pkaddr=%x\n",
57331908Skarels 			(unsigned) sel2,
57431908Skarels 			(unsigned) sel4,
57531908Skarels 			(unsigned) sel6,
57631908Skarels 			(unsigned) sel10,
57731908Skarels 			(unsigned) pkaddr
57834529Skarels 		));
57931908Skarels 		if((sc->sc_flag & DMV_RUNNING)==0) {
58031908Skarels 				log(LOG_WARNING, "dmvxint: dmv%d xint while down\n", unit);
58131908Skarels 				return;
58231908Skarels 		}
58331908Skarels 		switch (sel2 & 07) {
58431908Skarels 		case DMV_BDRUS:
58531908Skarels 			/*
58631908Skarels 			 * A read has completed.
58731908Skarels 			 * Pass packet to type specific
58831908Skarels 			 * higher-level input routine.
58931908Skarels 			 */
59031908Skarels 			ifp->if_ipackets++;
59131908Skarels 			/* find location in dmvuba struct */
59231908Skarels 			ifrw= &sc->sc_ifr[0];
59331908Skarels 			for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
59431908Skarels 				if(rp->ubinfo == pkaddr)
59531908Skarels 					break;
59631908Skarels 				ifrw++;
59731908Skarels 			}
59831908Skarels 			if (rp >= &sc->sc_rbufs[NRCV])
59931908Skarels 				panic("dmv rcv");
60031908Skarels 			if ((rp->flags & DBUF_DMVS) == 0)
60131908Skarels 				log(LOG_WARNING, "dmvxint: dmv%d done unalloc rbuf\n", unit);
60231908Skarels 
60331908Skarels 			len = (sel10&0x3fff) - sizeof (struct dmv_header);
60431908Skarels 			if (len < 0 || len > DMVMTU) {
60531908Skarels 				ifp->if_ierrors++;
60631908Skarels 				log(LOG_ERR, "dmvxint: dmv%d bad rcv pkt addr 0x%x len 0x%x\n",
60731908Skarels 				    unit, pkaddr, len);
60831908Skarels 				goto setup;
60931908Skarels 			}
61031908Skarels 			/*
61131908Skarels 			 * Deal with trailer protocol: if type is trailer
61231908Skarels 			 * get true type from first 16-bit word past data.
61331908Skarels 			 * Remember that type was trailer by setting off.
61431908Skarels 			 */
61531908Skarels 			dh = (struct dmv_header *)ifrw->ifrw_addr;
61631908Skarels 			dh->dmv_type = ntohs((u_short)dh->dmv_type);
61731908Skarels #define dmvdataaddr(dh, off, type)	((type)(((caddr_t)((dh)+1)+(off))))
61831908Skarels 			if (dh->dmv_type >= DMV_TRAILER &&
61931908Skarels 			    dh->dmv_type < DMV_TRAILER+DMV_NTRAILER) {
62031908Skarels 				off = (dh->dmv_type - DMV_TRAILER) * 512;
62131908Skarels 				if (off >= DMVMTU)
62231908Skarels 					goto setup;		/* sanity */
62331908Skarels 				dh->dmv_type = ntohs(*dmvdataaddr(dh, off, u_short *));
62431908Skarels 				resid = ntohs(*(dmvdataaddr(dh, off+2, u_short *)));
62531908Skarels 				if (off + resid > len)
62631908Skarels 					goto setup;		/* sanity */
62731908Skarels 				len = off + resid;
62831908Skarels 			} else
62931908Skarels 				off = 0;
63031908Skarels 			if (len == 0)
63131908Skarels 				goto setup;
63231908Skarels 
63331908Skarels 			/*
63431908Skarels 			 * Pull packet off interface.  Off is nonzero if
63531908Skarels 			 * packet has trailing header; dmv_get will then
63631908Skarels 			 * force this header information to be at the front,
63731908Skarels 			 * but we still have to drop the type and length
63831908Skarels 			 * which are at the front of any trailer data.
63931908Skarels 			 */
64031908Skarels 			m = if_ubaget(&sc->sc_ifuba, ifrw, len, off, ifp);
64131908Skarels 			if (m == 0)
64231908Skarels 				goto setup;
643*36032Skarels 			if (off) {
644*36032Skarels 				ifp = *(mtod(m, struct ifnet **));
645*36032Skarels 				m->m_off += 2 * sizeof (u_short);
646*36032Skarels 				m->m_len -= 2 * sizeof (u_short);
647*36032Skarels 				*(mtod(m, struct ifnet **)) = ifp;
648*36032Skarels 			}
64931908Skarels 			switch (dh->dmv_type) {
65031908Skarels #ifdef INET
65131908Skarels 			case DMV_IPTYPE:
65231908Skarels 				schednetisr(NETISR_IP);
65331908Skarels 				inq = &ipintrq;
65431908Skarels 				break;
65531908Skarels #endif
65631908Skarels 			default:
65731908Skarels 				m_freem(m);
65831908Skarels 				goto setup;
65931908Skarels 			}
66031908Skarels 
66131908Skarels 			s = splimp();
66231908Skarels 			if (IF_QFULL(inq)) {
66331908Skarels 				IF_DROP(inq);
66431908Skarels 				m_freem(m);
66531908Skarels 			} else
66631908Skarels 				IF_ENQUEUE(inq, m);
66731908Skarels 			splx(s);
66831908Skarels 	setup:
66931908Skarels 			/* is this needed? */
670*36032Skarels 			rp->ubinfo = UBAI_ADDR(ifrw->ifrw_info);
67131908Skarels 			dmvload(
67231908Skarels 				sc,
67331908Skarels 				DMV_BACCR,
67431908Skarels 				QP_SEL4|QP_SEL6|QP_SEL10,
67531908Skarels 				0,
676*36032Skarels 				(u_short) rp->ubinfo,
67731908Skarels 				(rp->ubinfo>>16)&0x3f,
67831908Skarels 				rp->cc
67931908Skarels 			);
68031908Skarels 			break;
68131908Skarels 		case DMV_BDXSA:
68231908Skarels 			/*
68331908Skarels 			 * A write has completed, start another
68431908Skarels 			 * transfer if there is more data to send.
68531908Skarels 			 */
68631908Skarels 			ifp->if_opackets++;
68731908Skarels 			/* find associated dmvbuf structure */
68831908Skarels 			ifxp = &sc->sc_ifw[0];
68931908Skarels 			for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
69031908Skarels 				if(rp->ubinfo == pkaddr)
69131908Skarels 					break;
69231908Skarels 				ifxp++;
69331908Skarels 			}
69431908Skarels 			if (rp >= &sc->sc_xbufs[NXMT]) {
69531908Skarels 				log(LOG_ERR, "dmv%d: bad packet address 0x%x\n",
69631908Skarels 				    unit, pkaddr);
69731908Skarels 				break;
69831908Skarels 			}
69931908Skarels 			if ((rp->flags & DBUF_DMVS) == 0)
70031908Skarels 				log(LOG_ERR, "dmvxint: dmv%d unallocated packet 0x%x\n",
70131908Skarels 				    unit, pkaddr);
70231908Skarels 			/* mark buffer free */
70331908Skarels 			if (ifxp->ifw_xtofree) {
70431908Skarels 				(void)m_freem(ifxp->ifw_xtofree);
70531908Skarels 				ifxp->ifw_xtofree = 0;
70631908Skarels 			}
70731908Skarels 			rp->flags &= ~DBUF_DMVS;
70834529Skarels 			if (--sc->sc_oused == 0)
70934529Skarels 				sc->sc_if.if_timer = 0;
71034529Skarels 			else
71134529Skarels 				sc->sc_if.if_timer = dmv_timeout;
71234529Skarels 			if ((sc->sc_flag & DMV_ONLINE) == 0) {
71334529Skarels 				extern int ifqmaxlen;
71434529Skarels 
71534529Skarels 				/*
71634529Skarels 				 * We're on the air.
71734529Skarels 				 * Open the queue to the usual value.
71834529Skarels 				 */
71934529Skarels 				sc->sc_flag |= DMV_ONLINE;
72034529Skarels 				ifp->if_snd.ifq_maxlen = ifqmaxlen;
72134529Skarels 			}
72231908Skarels 			break;
72331908Skarels 
72431908Skarels 		case DMV_CNTRLO:
72531908Skarels 			/* ACCUMULATE STATISTICS */
72631908Skarels 			switch(sel6&DMV_EEC) {
72731908Skarels 			case DMV_ORUN:
72831908Skarels 				if(sc->sc_flag & DMV_RESTART) {
72931908Skarels 					load_rec_bufs(sc);
73031908Skarels 					sc->sc_flag &= ~DMV_RESTART;
73131908Skarels 					log(LOG_INFO,
73234529Skarels 					    "dmv%d: far end on-line\n", unit);
73331908Skarels 				} else {
73431908Skarels 					log(LOG_WARNING,
73534529Skarels 					    "dmv%d: far end restart\n", unit);
73634529Skarels 					goto restart;
73731908Skarels 				}
73831908Skarels 				break;
73931908Skarels 			case DMV_RTE:
74031908Skarels 				ifp->if_ierrors++;
74131909Skarels 				if ((sc->sc_rte++ % DMV_RPRTE) == 0)
74231909Skarels 					log(LOG_WARNING,
74334529Skarels 				    "dmv%d: receive threshold error\n",
74431909Skarels 					    unit);
74531908Skarels 				break;
74631908Skarels 			case DMV_TTE:
74731908Skarels 				ifp->if_oerrors++;
74831909Skarels 				if ((sc->sc_xte++ % DMV_RPTTE) == 0)
74931909Skarels 					log(LOG_WARNING,
75034529Skarels 				    "dmv%d: transmit threshold error\n",
75131909Skarels 					    unit);
75231908Skarels 				break;
75331908Skarels 			case DMV_STE:
75431909Skarels 				if ((sc->sc_ste++ % DMV_RPSTE) == 0)
75531909Skarels 					log(LOG_WARNING,
75634529Skarels 				    "dmv%d: select threshold error\n",
75731909Skarels 					    unit);
75831908Skarels 				break;
75931908Skarels 			case DMV_NXM:
76031909Skarels 				if ((sc->sc_nxm++ % DMV_RPNXM) == 0)
76131909Skarels 					log(LOG_WARNING,
76234529Skarels 				    "dmv%d: nonexistent memory error\n",
76331909Skarels 					    unit);
76431908Skarels 				break;
76531908Skarels 			case DMV_MODD:
76634529Skarels 				if ((sc->sc_modd++ % DMV_RPMODD) == 0) {
76731909Skarels 					log(LOG_WARNING,
76834529Skarels 				    "dmv%d: modem disconnected error\n",
76931909Skarels 					    unit);
77034529Skarels 					goto restart;
77134529Skarels 				}
77231908Skarels 				break;
77331908Skarels 			case DMV_CXRL:
77431909Skarels 				if ((sc->sc_cxrl++ % DMV_RPCXRL) == 0)
77531909Skarels 					log(LOG_WARNING,
77634529Skarels 				    "dmv%d: carrier loss error\n",
77731909Skarels 					    unit);
77831908Skarels 				break;
77931908Skarels 			case DMV_QOVF:
78031908Skarels 				log(LOG_WARNING,
78134529Skarels 				    "dmv%d: response queue overflow\n",
78234529Skarels 				    unit);
78331908Skarels 				sc->sc_qovf++;
78434529Skarels 				goto restart;
78531908Skarels 
78631908Skarels 			default:
78731908Skarels 				log(LOG_WARNING,
78834529Skarels 				    "dmv%d: unknown error %o\n",
78934529Skarels 				    unit, sel6&DMV_EEC);
79031908Skarels 				if ((sc->sc_unknown++ % DMV_RPUNKNOWN) == 0)
79134529Skarels 					goto restart;
79231908Skarels 				break;
79331908Skarels 			}
79431908Skarels 			break;
79531908Skarels 
79631908Skarels 		case DMV_BDRUNUS:
79731908Skarels 		case DMV_BDXSN:
79831908Skarels 		case DMV_BDXNS:
79931908Skarels 			log(LOG_INFO,
80034529Skarels 			   "dmv%d: buffer disp for halted trib %o\n",
80131908Skarels 			   unit, sel2&0x7
80231908Skarels 		        );
80331908Skarels 			break;
80431908Skarels 
80531908Skarels 		case DMV_MDEFO:
80631908Skarels 			if((sel6&0x1f) == 020) {
80731908Skarels 				log(LOG_INFO,
80834529Skarels 			   		"dmv%d: buffer return complete sel3=%x\n",
80931908Skarels 			   		unit, sel3);
81031908Skarels 			} else {
81131908Skarels 				log(LOG_INFO,
81234529Skarels 			   	"dmv%d: info resp sel3=%x sel4=%x sel6=%x\n",
81331908Skarels 			   	unit, sel3, sel4, sel6
81431908Skarels 		        	);
81531908Skarels 			}
81631908Skarels 			break;
81731908Skarels 
81831908Skarels 		default:
81934529Skarels 			log(LOG_WARNING, "dmv%d: bad control %o\n",
82031908Skarels 			   unit, sel2&0x7
82131908Skarels 		        );
82231908Skarels 			break;
82331908Skarels 		}
82431908Skarels 	}
82531908Skarels 	dmvstart(unit);
82631908Skarels 	return;
82734529Skarels restart:
82831908Skarels 	dmvrestart(unit);
82931908Skarels }
83034529Skarels 
83131908Skarels load_rec_bufs(sc)
83231908Skarels register struct dmv_softc *sc;
83331908Skarels {
83431908Skarels 	register struct dmvbufs *rp;
83531908Skarels 
83631908Skarels 	/* queue first NRCV buffers for DMV to fill */
83731908Skarels 	for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
83831908Skarels 		rp->flags |= DBUF_DMVS;
83931908Skarels 		dmvload(
84031908Skarels 			sc,
84131908Skarels 			DMV_BACCR,
84231908Skarels 			QP_TRIB|QP_SEL4|QP_SEL6|QP_SEL10,
84331908Skarels 			1,
84431908Skarels 			rp->ubinfo,
84531908Skarels 			(rp->ubinfo>>16)&0x3f,
84631908Skarels 			rp->cc
84731908Skarels 		);
84831908Skarels 		sc->sc_iused++;
84931908Skarels 	}
85031908Skarels }
85131908Skarels 
85231908Skarels /*
85331908Skarels  * DMV output routine.
85431908Skarels  * Encapsulate a packet of type family for the dmv.
85531908Skarels  * Use trailer local net encapsulation if enough data in first
85631908Skarels  * packet leaves a multiple of 512 bytes of data in remainder.
85731908Skarels  */
85831908Skarels dmvoutput(ifp, m0, dst)
85931908Skarels 	register struct ifnet *ifp;
86031908Skarels 	register struct mbuf *m0;
86131908Skarels 	struct sockaddr *dst;
86231908Skarels {
86331908Skarels 	int type, error, s;
86431908Skarels 	register struct mbuf *m = m0;
86531908Skarels 	register struct dmv_header *dh;
86631908Skarels 	register int off;
86731908Skarels 
86834529Skarels 	if ((ifp->if_flags & IFF_UP) == 0) {
86934529Skarels 		error = ENETDOWN;
87034529Skarels 		goto bad;
87134529Skarels 	}
87234529Skarels 
87331908Skarels 	switch (dst->sa_family) {
87431908Skarels #ifdef	INET
87531908Skarels 	case AF_INET:
876*36032Skarels 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
87731908Skarels 		if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
87831908Skarels 		if (off > 0 && (off & 0x1ff) == 0 &&
879*36032Skarels 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
88031908Skarels 			type = DMV_TRAILER + (off>>9);
881*36032Skarels 			m->m_off -= 2 * sizeof (u_short);
88231908Skarels 			m->m_len += 2 * sizeof (u_short);
88331908Skarels 			*mtod(m, u_short *) = htons((u_short)DMV_IPTYPE);
88431908Skarels 			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
88531908Skarels 			goto gottrailertype;
88631908Skarels 		}
88731908Skarels 		type = DMV_IPTYPE;
88831908Skarels 		off = 0;
88931908Skarels 		goto gottype;
89031908Skarels #endif
89131908Skarels 
89231908Skarels 	case AF_UNSPEC:
89331908Skarels 		dh = (struct dmv_header *)dst->sa_data;
89431908Skarels 		type = dh->dmv_type;
89531908Skarels 		goto gottype;
89631908Skarels 
89731908Skarels 	default:
89834529Skarels 		log(LOG_ERR, "dmvoutput, dmv%d can't handle af%d\n",
89934529Skarels 		    ifp->if_unit, dst->sa_family);
90031908Skarels 		error = EAFNOSUPPORT;
90131908Skarels 		goto bad;
90231908Skarels 	}
90331908Skarels 
90431908Skarels gottrailertype:
90531908Skarels 	/*
90631908Skarels 	 * Packet to be sent as a trailer; move first packet
90731908Skarels 	 * (control information) to end of chain.
90831908Skarels 	 */
90931908Skarels 	while (m->m_next)
91031908Skarels 		m = m->m_next;
91131908Skarels 	m->m_next = m0;
91231908Skarels 	m = m0->m_next;
91331908Skarels 	m0->m_next = 0;
91431908Skarels 	m0 = m;
91531908Skarels 
91631908Skarels gottype:
91731908Skarels 	/*
91831908Skarels 	 * Add local network header
91931908Skarels 	 * (there is space for a uba on a vax to step on)
92031908Skarels 	 */
921*36032Skarels 	if (m->m_off > MMAXOFF ||
922*36032Skarels 	    MMINOFF + sizeof(struct dmv_header) > m->m_off) {
923*36032Skarels 		m = m_get(M_DONTWAIT, MT_HEADER);
924*36032Skarels 		if (m == 0) {
925*36032Skarels 			error = ENOBUFS;
926*36032Skarels 			goto bad;
927*36032Skarels 		}
928*36032Skarels 		m->m_next = m0;
929*36032Skarels 		m->m_off = MMINOFF;
930*36032Skarels 		m->m_len = sizeof (struct dmv_header);
931*36032Skarels 	} else {
932*36032Skarels 		m->m_off -= sizeof (struct dmv_header);
933*36032Skarels 		m->m_len += sizeof (struct dmv_header);
93431908Skarels 	}
93531908Skarels 	dh = mtod(m, struct dmv_header *);
93631908Skarels 	dh->dmv_type = htons((u_short)type);
93731908Skarels 
93831908Skarels 	/*
93931908Skarels 	 * Queue message on interface, and start output if interface
94031908Skarels 	 * not yet active.
94131908Skarels 	 */
94231908Skarels 	s = splimp();
94331908Skarels 	if (IF_QFULL(&ifp->if_snd)) {
94431908Skarels 		IF_DROP(&ifp->if_snd);
94531908Skarels 		m_freem(m);
94631908Skarels 		splx(s);
94731908Skarels 		return (ENOBUFS);
94831908Skarels 	}
94931908Skarels 	IF_ENQUEUE(&ifp->if_snd, m);
95031908Skarels 	dmvstart(ifp->if_unit);
95131908Skarels 	splx(s);
95231908Skarels 	return (0);
95331908Skarels 
95431908Skarels bad:
95531908Skarels 	m_freem(m0);
95631908Skarels 	return (error);
95731908Skarels }
95831908Skarels 
95931908Skarels 
96031908Skarels /*
96131908Skarels  * Process an ioctl request.
96231908Skarels  */
96331908Skarels /* ARGSUSED */
96431908Skarels dmvioctl(ifp, cmd, data)
96531908Skarels 	register struct ifnet *ifp;
96631908Skarels 	int cmd;
96731908Skarels 	caddr_t data;
96831908Skarels {
96931908Skarels 	int s = splimp(), error = 0;
97031908Skarels 	struct mbuf *m;
97131908Skarels 	register struct dmv_softc *sc = &dmv_softc[ifp->if_unit];
97231908Skarels 
97331908Skarels 	switch (cmd) {
97431908Skarels 
97531908Skarels 	case SIOCSIFADDR:
97631908Skarels 		ifp->if_flags |= IFF_UP;
97731908Skarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
97831908Skarels 			dmvinit(ifp->if_unit);
97931908Skarels 		break;
98031908Skarels 
98131908Skarels 	case SIOCSIFDSTADDR:
98231908Skarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
98331908Skarels 			dmvinit(ifp->if_unit);
98431908Skarels 		break;
98531908Skarels 
98631908Skarels 	case SIOCSIFFLAGS:
98731908Skarels 		if ((ifp->if_flags & IFF_UP) == 0 &&
98834529Skarels 		    sc->sc_flag & DMV_RUNNING)
98934529Skarels 			dmvdown(ifp->if_unit);
99034529Skarels 		else if (ifp->if_flags & IFF_UP &&
99131908Skarels 		    (sc->sc_flag & DMV_RUNNING) == 0)
99231908Skarels 			dmvrestart(ifp->if_unit);
99331908Skarels 		break;
99431908Skarels 
99531908Skarels 	default:
99631908Skarels 		error = EINVAL;
99731908Skarels 	}
99831908Skarels 	splx(s);
99931908Skarels 	return (error);
100031908Skarels }
100131908Skarels 
100231908Skarels /*
100331908Skarels  * Restart after a fatal error.
100431908Skarels  * Clear device and reinitialize.
100531908Skarels  */
100631908Skarels dmvrestart(unit)
100731908Skarels 	int unit;
100831908Skarels {
100931908Skarels 	register struct dmvdevice *addr;
101031908Skarels 	register int i;
101134529Skarels 
101234529Skarels 	dmvdown(unit);
101334529Skarels 
101434529Skarels 	addr = (struct dmvdevice *)(dmvinfo[unit]->ui_addr);
101531908Skarels 	/*
101634529Skarels 	 * Let the DMV finish the MCLR.
101731908Skarels 	 */
101831908Skarels 	for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--)
101931908Skarels 		;
102031908Skarels 	if ((addr->bsel1 & DMV_RUN) == 0) {
102131908Skarels 		log(LOG_ERR, "dmvrestart: can't start device\n" );
102231908Skarels 		return (0);
102331908Skarels 	}
102431908Skarels 	if ((addr->bsel4 != 033) || (addr->bsel6 != 0305))
102531908Skarels 	{
102634529Skarels 		log(LOG_ERR, "dmv%d: device init failed, bsel4=%o, bsel6=%o\n",
102734529Skarels 			unit, addr->bsel4, addr->bsel6);
102831908Skarels 		return (0);
102931908Skarels 	}
103034529Skarels 
103134529Skarels 	/* restart DMV */
103234529Skarels 	dmvinit(unit);
103334529Skarels 	dmv_softc[unit].sc_if.if_collisions++;	/* why not? */
103434529Skarels }
103534529Skarels 
103634529Skarels /*
103734529Skarels  * Reset a device and mark down.
103834529Skarels  * Flush output queue and drop queue limit.
103934529Skarels  */
104034529Skarels dmvdown(unit)
104134529Skarels 	int unit;
104234529Skarels {
104334529Skarels 	struct dmv_softc *sc = &dmv_softc[unit];
104434529Skarels 	register struct ifxmt *ifxp;
104534529Skarels 
104634529Skarels 	((struct dmvdevice *)(dmvinfo[unit]->ui_addr))->bsel1 = DMV_MCLR;
104734529Skarels 	sc->sc_flag &= ~(DMV_RUNNING | DMV_ONLINE);
104834529Skarels 
104931908Skarels 	for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) {
105031908Skarels 		if (ifxp->ifw_xtofree) {
105131908Skarels 			(void) m_freem(ifxp->ifw_xtofree);
105231908Skarels 			ifxp->ifw_xtofree = 0;
105331908Skarels 		}
105431908Skarels 	}
105534529Skarels 	sc->sc_oused = 0;
105634529Skarels 	if_qflush(&sc->sc_if.if_snd);
105734529Skarels 
105834529Skarels 	/*
105934529Skarels 	 * Limit packets enqueued until we're back on the air.
106034529Skarels 	 */
106134529Skarels 	sc->sc_if.if_snd.ifq_maxlen = 3;
106231908Skarels }
106331908Skarels 
106431908Skarels /*
106534529Skarels  * Watchdog timeout to see that transmitted packets don't
106634529Skarels  * lose interrupts.  The device has to be online.
106731908Skarels  */
106834529Skarels dmvtimeout(unit)
106934529Skarels 	int unit;
107031908Skarels {
107131908Skarels 	register struct dmv_softc *sc;
107231908Skarels 	struct dmvdevice *addr;
107331908Skarels 
107434529Skarels 	sc = &dmv_softc[unit];
107534529Skarels 	if (sc->sc_flag & DMV_ONLINE) {
107634529Skarels 		addr = (struct dmvdevice *)(dmvinfo[unit]->ui_addr);
107734529Skarels 		log(LOG_ERR, "dmv%d: output timeout, bsel0=%b bsel2=%b\n",
107834529Skarels 		    unit, addr->bsel0 & 0xff, DMV0BITS,
107934529Skarels 		    addr->bsel2 & 0xff, DMV2BITS);
108034529Skarels 		dmvrestart(unit);
108131908Skarels 	}
108231908Skarels }
108331908Skarels #endif
1084