xref: /csrg-svn/sys/vax/if/if_dmc.c (revision 13065)
1*13065Ssam /*	if_dmc.c	4.28	83/06/13	*/
25725Sroot 
35725Sroot #include "dmc.h"
45725Sroot #if NDMC > 0
55725Sroot #define printd if(dmcdebug)printf
611189Ssam int dmcdebug = 0;
75725Sroot /*
85725Sroot  * DMC11 device driver, internet version
95725Sroot  *
105725Sroot  * TODO
115725Sroot  *	allow more than one outstanding read or write.
1211191Ssam  *
1313061Ssam  * UNTESTED WITH 4.2
145725Sroot  */
159794Ssam #include "../machine/pte.h"
165725Sroot 
175725Sroot #include "../h/param.h"
185725Sroot #include "../h/systm.h"
195725Sroot #include "../h/mbuf.h"
205725Sroot #include "../h/buf.h"
215725Sroot #include "../h/tty.h"
225725Sroot #include "../h/protosw.h"
235725Sroot #include "../h/socket.h"
245725Sroot #include "../h/vmmac.h"
2513061Ssam #include "../h/ioctl.h"
2613061Ssam #include "../h/errno.h"
278460Sroot 
288460Sroot #include "../net/if.h"
299176Ssam #include "../net/netisr.h"
308460Sroot #include "../net/route.h"
318416Swnj #include "../netinet/in.h"
328416Swnj #include "../netinet/in_systm.h"
338460Sroot 
348460Sroot #include "../vax/cpu.h"
358460Sroot #include "../vax/mtpr.h"
368416Swnj #include "../vaxif/if_uba.h"
378416Swnj #include "../vaxif/if_dmc.h"
388460Sroot #include "../vaxuba/ubareg.h"
398460Sroot #include "../vaxuba/ubavar.h"
405725Sroot 
4111189Ssam #ifndef DMC_USEMAINT
4211189Ssam #define	DMC_USEMAINT	1	/* use maintenance mode */
4311189Ssam #endif
4411189Ssam 
455725Sroot /*
465725Sroot  * Driver information for auto-configuration stuff.
475725Sroot  */
4813061Ssam int	dmcprobe(), dmcattach(), dmcinit(), dmcioctl();
4913061Ssam int	dmcoutput(), dmcreset();
505725Sroot struct	uba_device *dmcinfo[NDMC];
515725Sroot u_short	dmcstd[] = { 0 };
525725Sroot struct	uba_driver dmcdriver =
535725Sroot 	{ dmcprobe, 0, dmcattach, 0, dmcstd, "dmc", dmcinfo };
545725Sroot 
555725Sroot /*
565725Sroot  * DMC software status per interface.
575725Sroot  *
585725Sroot  * Each interface is referenced by a network interface structure,
595725Sroot  * sc_if, which the routing code uses to locate the interface.
605725Sroot  * This structure contains the output queue for the interface, its address, ...
615725Sroot  * We also have, for each interface, a UBA interface structure, which
625725Sroot  * contains information about the UNIBUS resources held by the interface:
635725Sroot  * map registers, buffered data paths, etc.  Information is cached in this
645725Sroot  * structure for use by the if_uba.c routines in running the interface
655725Sroot  * efficiently.
665725Sroot  */
675725Sroot struct dmc_softc {
685725Sroot 	struct	ifnet sc_if;		/* network-visible interface */
695725Sroot 	struct	ifuba sc_ifuba;		/* UNIBUS resources */
705725Sroot 	short	sc_flag;		/* flags */
715725Sroot 	short	sc_oactive;		/* output active */
725725Sroot 	int	sc_ubinfo;		/* UBA mapping info for base table */
735725Sroot 	struct clist sc_que;		/* command queue */
745725Sroot } dmc_softc[NDMC];
755725Sroot 
765725Sroot /* flags */
775725Sroot #define	DMCRUN		01
785725Sroot #define	DMCBMAPPED	02		/* base table mapped */
795725Sroot 
805725Sroot struct dmc_base {
815725Sroot 	short	d_base[128];		/* DMC base table */
825725Sroot } dmc_base[NDMC];
835725Sroot 
845725Sroot #define	loword(x)	((short *)&x)[0]
855725Sroot #define	hiword(x)	((short *)&x)[1]
865725Sroot 
875725Sroot dmcprobe(reg)
885725Sroot 	caddr_t reg;
895725Sroot {
905725Sroot 	register int br, cvec;
915725Sroot 	register struct dmcdevice *addr = (struct dmcdevice *)reg;
925725Sroot 	register int i;
935725Sroot 
945725Sroot #ifdef lint
955725Sroot 	br = 0; cvec = br; br = cvec;
965725Sroot 	dmcrint(0); dmcxint(0);
975725Sroot #endif
985725Sroot 	addr->bsel1 = DMC_MCLR;
995725Sroot 	for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--)
1005725Sroot 		;
1015725Sroot 	if ((addr->bsel1 & DMC_RUN) == 0)
1026334Ssam 		return (0);
1035725Sroot 	addr->bsel1 &= ~DMC_MCLR;
1045725Sroot 	addr->bsel0 = DMC_RQI|DMC_IEI;
1055725Sroot 	DELAY(100000);
1065725Sroot 	addr->bsel1 = DMC_MCLR;
1075725Sroot 	for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--)
1085725Sroot 		;
1096575Ssam #ifdef ECHACK
1106575Ssam 	br = 0x16;
1116575Ssam #endif
1126334Ssam 	return (1);
1135725Sroot }
1145725Sroot 
1155725Sroot /*
1165725Sroot  * Interface exists: make available by filling in network interface
1175725Sroot  * record.  System will initialize the interface when it is ready
1185725Sroot  * to accept packets.
1195725Sroot  */
1205725Sroot dmcattach(ui)
1215725Sroot 	register struct uba_device *ui;
1225725Sroot {
1235725Sroot 	register struct dmc_softc *sc = &dmc_softc[ui->ui_unit];
1245725Sroot 
1255725Sroot 	sc->sc_if.if_unit = ui->ui_unit;
1265725Sroot 	sc->sc_if.if_name = "dmc";
1275725Sroot 	sc->sc_if.if_mtu = DMCMTU;
1285725Sroot 	sc->sc_if.if_init = dmcinit;
1295725Sroot 	sc->sc_if.if_output = dmcoutput;
13013061Ssam 	sc->sc_if.if_ioctl = dmcioctl;
1318976Sroot 	sc->sc_if.if_reset = dmcreset;
1326587Ssam 	/* DON'T KNOW IF THIS WILL WORK WITH A BDP AT HIGH SPEEDS */
1336587Ssam 	sc->sc_ifuba.ifu_flags = UBA_NEEDBDP | UBA_CANTWAIT;
1345725Sroot 	if_attach(&sc->sc_if);
1355725Sroot }
1365725Sroot 
1375725Sroot /*
1385725Sroot  * Reset of interface after UNIBUS reset.
1395725Sroot  * If interface is on specified UBA, reset it's state.
1405725Sroot  */
1415725Sroot dmcreset(unit, uban)
1425725Sroot 	int unit, uban;
1435725Sroot {
1445725Sroot 	register struct uba_device *ui;
1455725Sroot 
1465725Sroot 	if (unit >= NDMC || (ui = dmcinfo[unit]) == 0 || ui->ui_alive == 0 ||
1475725Sroot 	    ui->ui_ubanum != uban)
1485725Sroot 		return;
1495725Sroot 	printf(" dmc%d", unit);
1505725Sroot 	dmcinit(unit);
1515725Sroot }
1525725Sroot 
1535725Sroot /*
1545725Sroot  * Initialization of interface; reinitialize UNIBUS usage.
1555725Sroot  */
1565725Sroot dmcinit(unit)
1575725Sroot 	int unit;
1585725Sroot {
1595725Sroot 	register struct dmc_softc *sc = &dmc_softc[unit];
1605725Sroot 	register struct uba_device *ui = dmcinfo[unit];
1615725Sroot 	register struct dmcdevice *addr;
16213061Ssam 	register struct ifnet *ifp = &sc->sc_if;
16313061Ssam 	struct sockaddr_in *sin;
1645725Sroot 	int base;
1655725Sroot 
1665725Sroot 	printd("dmcinit\n");
16713061Ssam 	sin = (struct sockaddr_in *)&ifp->if_addr;
16813061Ssam 	if (sin->sin_addr.s_addr == 0)
1695725Sroot 		return;
17013061Ssam 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
17113061Ssam 		if ((sc->sc_flag&DMCBMAPPED) == 0) {
17213061Ssam 			sc->sc_ubinfo = uballoc(ui->ui_ubanum,
17313061Ssam 			    (caddr_t)&dmc_base[unit],
17413061Ssam 			    sizeof (struct dmc_base), 0);
17513061Ssam 			sc->sc_flag |= DMCBMAPPED;
17613061Ssam 		}
17713061Ssam 		if (if_ubainit(&sc->sc_ifuba, ui->ui_ubanum, 0,
17813061Ssam 		    (int)btoc(DMCMTU)) == 0) {
17913061Ssam 			printf("dmc%d: can't initialize\n", unit);
18013061Ssam 			ifp->if_flags &= ~IFF_UP;
18113061Ssam 			return;
18213061Ssam 		}
18313061Ssam 		addr = (struct dmcdevice *)ui->ui_addr;
18413061Ssam 		addr->bsel2 |= DMC_IEO;
18513061Ssam 		base = sc->sc_ubinfo & 0x3ffff;
18613061Ssam 		printd("  base 0x%x\n", base);
18713061Ssam 		dmcload(sc, DMC_BASEI, base, (base>>2)&DMC_XMEM);
18813061Ssam 		dmcload(sc, DMC_CNTLI, 0, DMC_USEMAINT ? DMC_MAINT : 0);
18913061Ssam 		base = sc->sc_ifuba.ifu_r.ifrw_info & 0x3ffff;
19013061Ssam 		dmcload(sc, DMC_READ, base, ((base>>2)&DMC_XMEM)|DMCMTU);
19113061Ssam 		printd("  first read queued, addr 0x%x\n", base);
19213061Ssam 		ifp->if_flags |= IFF_UP|IFF_RUNNING;
1935725Sroot 	}
1946363Ssam 	/* set up routing table entry */
19513061Ssam 	if ((ifp->if_flags & IFF_ROUTE) == 0) {
19613061Ssam 		rtinit(sin, sin, RTF_HOST|RTF_UP);
19713061Ssam 		ifp->if_flags |= IFF_ROUTE;
1986363Ssam 	}
1995725Sroot }
2005725Sroot 
2015725Sroot /*
2025725Sroot  * Start output on interface.  Get another datagram
2035725Sroot  * to send from the interface queue and map it to
2045725Sroot  * the interface before starting output.
2055725Sroot  */
2065725Sroot dmcstart(dev)
2075725Sroot 	dev_t dev;
2085725Sroot {
2095725Sroot 	int unit = minor(dev);
2105725Sroot 	register struct dmc_softc *sc = &dmc_softc[unit];
2115725Sroot 	int addr, len;
2125725Sroot 	struct mbuf *m;
2135725Sroot 
2145725Sroot 	printd("dmcstart\n");
2155725Sroot 	/*
2165725Sroot 	 * Dequeue a request and map it to the UNIBUS.
2175725Sroot 	 * If no more requests, just return.
2185725Sroot 	 */
2195725Sroot 	IF_DEQUEUE(&sc->sc_if.if_snd, m);
2205725Sroot 	if (m == 0)
2215725Sroot 		return;
2225725Sroot 	len = if_wubaput(&sc->sc_ifuba, m);
2235725Sroot 
2245725Sroot 	/*
2255725Sroot 	 * Have request mapped to UNIBUS for transmission.
2265725Sroot 	 * Purge any stale data from this BDP and start the output.
2275725Sroot 	 */
2286587Ssam 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
2295853Sroot 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp);
2305725Sroot 	addr = sc->sc_ifuba.ifu_w.ifrw_info & 0x3ffff;
2315725Sroot 	printd("  len %d, addr 0x%x, ", len, addr);
2325725Sroot 	printd("mr 0x%x\n", sc->sc_ifuba.ifu_w.ifrw_mr[0]);
2335725Sroot 	dmcload(sc, DMC_WRITE, addr, (len&DMC_CCOUNT)|((addr>>2)&DMC_XMEM));
2345725Sroot 	sc->sc_oactive = 1;
2355725Sroot }
2365725Sroot 
2375725Sroot /*
2385725Sroot  * Utility routine to load the DMC device registers.
2395725Sroot  */
2405725Sroot dmcload(sc, type, w0, w1)
2415725Sroot 	register struct dmc_softc *sc;
2425725Sroot 	int type, w0, w1;
2435725Sroot {
2445725Sroot 	register struct dmcdevice *addr;
2455725Sroot 	register int unit, sps, n;
2465725Sroot 
2475725Sroot 	printd("dmcload: 0x%x 0x%x 0x%x\n", type, w0, w1);
2485725Sroot 	unit = sc - dmc_softc;
2495725Sroot 	addr = (struct dmcdevice *)dmcinfo[unit]->ui_addr;
2505725Sroot 	sps = spl5();
2515725Sroot 	if ((n = sc->sc_que.c_cc) == 0)
2525725Sroot 		addr->bsel0 = type | DMC_RQI;
2535725Sroot 	else
2546159Ssam 		(void) putc(type | DMC_RQI, &sc->sc_que);
2556159Ssam 	(void) putw(w0, &sc->sc_que);
2566159Ssam 	(void) putw(w1, &sc->sc_que);
2575725Sroot 	if (n == 0)
2585725Sroot 		dmcrint(unit);
2595725Sroot 	splx(sps);
2605725Sroot }
2615725Sroot 
2625725Sroot /*
2635725Sroot  * DMC interface receiver interrupt.
2645725Sroot  * Ready to accept another command,
2655725Sroot  * pull one off the command queue.
2665725Sroot  */
2675725Sroot dmcrint(unit)
2685725Sroot 	int unit;
2695725Sroot {
2705725Sroot 	register struct dmc_softc *sc;
2715725Sroot 	register struct dmcdevice *addr;
2725725Sroot 	register int n;
2735725Sroot 
2745725Sroot 	addr = (struct dmcdevice *)dmcinfo[unit]->ui_addr;
2755725Sroot 	sc = &dmc_softc[unit];
2765725Sroot 	while (addr->bsel0&DMC_RDYI) {
2775725Sroot 		addr->sel4 = getw(&sc->sc_que);
2785725Sroot 		addr->sel6 = getw(&sc->sc_que);
2795725Sroot 		addr->bsel0 &= ~(DMC_IEI|DMC_RQI);
2805725Sroot 		while (addr->bsel0&DMC_RDYI)
2815725Sroot 			;
2825725Sroot 		if (sc->sc_que.c_cc == 0)
28311189Ssam 			goto out;
2845725Sroot 		addr->bsel0 = getc(&sc->sc_que);
2855725Sroot 		n = RDYSCAN;
2865725Sroot 		while (n-- && (addr->bsel0&DMC_RDYI) == 0)
2875725Sroot 			;
2885725Sroot 	}
2895725Sroot 	if (sc->sc_que.c_cc)
2905725Sroot 		addr->bsel0 |= DMC_IEI;
29111189Ssam out:
29211206Ssam 	dmcxint(unit);
2935725Sroot }
2945725Sroot 
2955725Sroot /*
2965725Sroot  * DMC interface transmitter interrupt.
2975725Sroot  * A transfer has completed, check for errors.
2985725Sroot  * If it was a read, notify appropriate protocol.
2995725Sroot  * If it was a write, pull the next one off the queue.
3005725Sroot  */
3015725Sroot dmcxint(unit)
3025725Sroot 	int unit;
3035725Sroot {
3045725Sroot 	register struct dmc_softc *sc;
305*13065Ssam 	register struct ifnet *ifp;
3065725Sroot 	struct uba_device *ui = dmcinfo[unit];
3075725Sroot 	struct dmcdevice *addr;
3085725Sroot 	struct mbuf *m;
3095725Sroot 	register struct ifqueue *inq;
31011189Ssam 	int arg, arg2, cmd, len;
3115725Sroot 
3125725Sroot 	addr = (struct dmcdevice *)ui->ui_addr;
31311189Ssam 	cmd = addr->bsel2 & 0xff;
31411189Ssam 	if ((cmd & DMC_RDYO) == 0)
31511189Ssam 		return;
31611189Ssam 	arg2 = addr->sel4;
3175725Sroot 	arg = addr->sel6;
3185725Sroot 	addr->bsel2 &= ~DMC_RDYO;
3195725Sroot 	sc = &dmc_softc[unit];
320*13065Ssam 	ifp = &sc->sc_if;
3215725Sroot 	printd("dmcxint\n");
32211189Ssam 	switch (cmd & 07) {
3235725Sroot 
3245725Sroot 	case DMC_OUR:
3255725Sroot 		/*
3265725Sroot 		 * A read has completed.  Purge input buffered
3275725Sroot 		 * data path.  Pass packet to type specific
3285725Sroot 		 * higher-level input routine.
3295725Sroot 		 */
330*13065Ssam 		ifp->if_ipackets++;
3316587Ssam 		if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
3325853Sroot 			UBAPURGE(sc->sc_ifuba.ifu_uba,
3335853Sroot 				sc->sc_ifuba.ifu_r.ifrw_bdp);
3345725Sroot 		len = arg & DMC_CCOUNT;
3355725Sroot 		printd("  read done, len %d\n", len);
336*13065Ssam 		switch (ifp->if_addr.sa_family) {
3375725Sroot #ifdef INET
3386334Ssam 		case AF_INET:
3396260Swnj 			schednetisr(NETISR_IP);
3405725Sroot 			inq = &ipintrq;
3415725Sroot 			break;
3425725Sroot #endif
3435725Sroot 
3445725Sroot 		default:
3456334Ssam 			printf("dmc%d: unknown address type %d\n", unit,
346*13065Ssam 			    ifp->if_addr.sa_family);
3475725Sroot 			goto setup;
3485725Sroot 		}
3495725Sroot 		m = if_rubaget(&sc->sc_ifuba, len, 0);
3505725Sroot 		if (m == 0)
3515725Sroot 			goto setup;
3526207Swnj 		if (IF_QFULL(inq)) {
3536207Swnj 			IF_DROP(inq);
3549176Ssam 			m_freem(m);
3556207Swnj 		} else
3566207Swnj 			IF_ENQUEUE(inq, m);
3575725Sroot 
3585725Sroot setup:
3595725Sroot 		arg = sc->sc_ifuba.ifu_r.ifrw_info & 0x3ffff;
3605725Sroot 		dmcload(sc, DMC_READ, arg, ((arg >> 2) & DMC_XMEM) | DMCMTU);
3615725Sroot 		return;
3625725Sroot 
3635725Sroot 	case DMC_OUX:
3645725Sroot 		/*
3655725Sroot 		 * A write has completed, start another
3665725Sroot 		 * transfer if there is more data to send.
3675725Sroot 		 */
3685725Sroot 		if (sc->sc_oactive == 0)
3695725Sroot 			return;		/* SHOULD IT BE A FATAL ERROR? */
3705725Sroot 		printd("  write done\n");
371*13065Ssam 		ifp->if_opackets++;
3725725Sroot 		sc->sc_oactive = 0;
3735725Sroot 		if (sc->sc_ifuba.ifu_xtofree) {
3749176Ssam 			m_freem(sc->sc_ifuba.ifu_xtofree);
3755725Sroot 			sc->sc_ifuba.ifu_xtofree = 0;
3765725Sroot 		}
377*13065Ssam 		if (ifp->if_snd.ifq_head == 0)
3785725Sroot 			return;
3795725Sroot 		dmcstart(unit);
3805725Sroot 		return;
3815725Sroot 
3825725Sroot 	case DMC_CNTLO:
3835725Sroot 		arg &= DMC_CNTMASK;
3845725Sroot 		if (arg&DMC_FATAL) {
3855725Sroot 			addr->bsel1 = DMC_MCLR;
3865725Sroot 			sc->sc_flag &= ~DMCRUN;
3875725Sroot 			/*** DO SOMETHING TO RESTART DEVICE ***/
3885725Sroot 			printf("DMC FATAL ERROR 0%o\n", arg);
3895725Sroot 		} else {
3905725Sroot 			/* ACCUMULATE STATISTICS */
3915725Sroot 			printf("DMC SOFT ERROR 0%o\n", arg);
3925725Sroot 		}
3935725Sroot 		return;
3945725Sroot 
3955725Sroot 	default:
3965725Sroot 		printf("dmc%d: bad control %o\n", unit, cmd);
3975725Sroot 	}
3985725Sroot }
3995725Sroot 
4005725Sroot /*
4015725Sroot  * DMC output routine.
4025725Sroot  * Just send the data, header was supplied by
4035725Sroot  * upper level protocol routines.
4045725Sroot  */
4056334Ssam dmcoutput(ifp, m, dst)
4065725Sroot 	register struct ifnet *ifp;
4075725Sroot 	register struct mbuf *m;
4086334Ssam 	struct sockaddr *dst;
4095725Sroot {
4105725Sroot 	struct uba_device *ui = dmcinfo[ifp->if_unit];
4115725Sroot 	int s;
4125725Sroot 
4135725Sroot 	printd("dmcoutput\n");
414*13065Ssam 	if (dst->sa_family != ifp->if_addr.sa_family) {
4159176Ssam 		printf("dmc%d: af%d not supported\n", ifp->if_unit,
416*13065Ssam 		    dst->sa_family);
4176334Ssam 		m_freem(m);
4186502Ssam 		return (EAFNOSUPPORT);
4195725Sroot 	}
4205725Sroot 	s = splimp();
4216207Swnj 	if (IF_QFULL(&ifp->if_snd)) {
4226207Swnj 		IF_DROP(&ifp->if_snd);
4236334Ssam 		m_freem(m);
4246207Swnj 		splx(s);
4256502Ssam 		return (ENOBUFS);
4266207Swnj 	}
4275725Sroot 	IF_ENQUEUE(&ifp->if_snd, m);
4285725Sroot 	if (dmc_softc[ifp->if_unit].sc_oactive == 0)
4295725Sroot 		dmcstart(ifp->if_unit);
4305725Sroot 	splx(s);
4316502Ssam 	return (0);
4325725Sroot }
43313061Ssam 
43413061Ssam /*
43513061Ssam  * Process an ioctl request.
43613061Ssam  */
43713061Ssam dmcioctl(ifp, cmd, data)
43813061Ssam 	register struct ifnet *ifp;
43913061Ssam 	int cmd;
44013061Ssam 	caddr_t data;
44113061Ssam {
44213061Ssam 	struct ifreq *ifr = (struct ifreq *)data;
44313061Ssam 	struct sockaddr_in *sin;
44413061Ssam 	int s = splimp(), error = 0;
44513061Ssam 
44613061Ssam 	switch (cmd) {
44713061Ssam 
44813061Ssam 	case SIOCSIFADDR:
44913061Ssam 		if (ifp->if_flags & IFF_RUNNING)
45013061Ssam 			if_rtinit(ifp, -1);	/* delete previous route */
45113061Ssam 		sin = (struct sockaddr_in *)&ifr->ifr_addr;
45213061Ssam 		ifp->if_addr = *sin;
45313061Ssam 		ifp->if_net = in_netof(sin->sin_addr);
45413061Ssam 		ifp->if_host[0] = in_lnaof(sin->sin_addr);
45513061Ssam 		dmcinit(ifp->if_unit);
45613061Ssam 		break;
45713061Ssam 
45813061Ssam 	case SIOCSIFDSTADDR:
45913061Ssam 		ifp->if_dstaddr = ifr->ifr_dstaddr;
46013061Ssam 		break;
46113061Ssam 
46213061Ssam 	default:
46313061Ssam 		error = EINVAL;
46413061Ssam 	}
46513061Ssam 	splx(s);
46613061Ssam 	return (error);
46713061Ssam }
468