xref: /csrg-svn/sys/vax/if/if_il.c (revision 45801)
123557Ssklower /*
229367Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
335323Sbostic  * All rights reserved.
423557Ssklower  *
544560Sbostic  * %sccs.include.redist.c%
635323Sbostic  *
7*45801Sbostic  *	@(#)if_il.c	7.8 (Berkeley) 12/16/90
823557Ssklower  */
96893Sfeldman 
106893Sfeldman #include "il.h"
1125274Sbloom #if NIL > 0
126893Sfeldman 
136893Sfeldman /*
146893Sfeldman  * Interlan Ethernet Communications Controller interface
156893Sfeldman  */
16*45801Sbostic #include "../include/pte.h"
179797Ssam 
18*45801Sbostic #include "sys/param.h"
19*45801Sbostic #include "sys/systm.h"
20*45801Sbostic #include "sys/mbuf.h"
21*45801Sbostic #include "sys/buf.h"
22*45801Sbostic #include "sys/protosw.h"
23*45801Sbostic #include "sys/socket.h"
24*45801Sbostic #include "sys/vmmac.h"
25*45801Sbostic #include "sys/ioctl.h"
26*45801Sbostic #include "sys/errno.h"
27*45801Sbostic #include "sys/syslog.h"
288463Sroot 
29*45801Sbostic #include "net/if.h"
30*45801Sbostic #include "net/netisr.h"
31*45801Sbostic #include "net/route.h"
3223557Ssklower 
3323557Ssklower #ifdef INET
34*45801Sbostic #include "netinet/in.h"
35*45801Sbostic #include "netinet/in_systm.h"
36*45801Sbostic #include "netinet/in_var.h"
37*45801Sbostic #include "netinet/ip.h"
38*45801Sbostic #include "netinet/if_ether.h"
3923557Ssklower #endif
4023557Ssklower 
4123557Ssklower #ifdef NS
42*45801Sbostic #include "netns/ns.h"
43*45801Sbostic #include "netns/ns_if.h"
4423557Ssklower #endif
4523557Ssklower 
46*45801Sbostic #include "../include/cpu.h"
47*45801Sbostic #include "../include/mtpr.h"
4817115Sbloom #include "if_il.h"
4917115Sbloom #include "if_ilreg.h"
5017115Sbloom #include "if_uba.h"
51*45801Sbostic #include "../uba/ubareg.h"
52*45801Sbostic #include "../uba/ubavar.h"
538463Sroot 
546893Sfeldman int	ilprobe(), ilattach(), ilrint(), ilcint();
556893Sfeldman struct	uba_device *ilinfo[NIL];
566893Sfeldman u_short ilstd[] = { 0 };
576893Sfeldman struct	uba_driver ildriver =
586893Sfeldman 	{ ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo };
596893Sfeldman #define	ILUNIT(x)	minor(x)
6037476Ssklower int	ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch(),ilstart();
6126167Skarels int	ildebug = 0;
626893Sfeldman 
636893Sfeldman /*
646893Sfeldman  * Ethernet software status per interface.
656893Sfeldman  *
666893Sfeldman  * Each interface is referenced by a network interface structure,
676893Sfeldman  * is_if, which the routing code uses to locate the interface.
686893Sfeldman  * This structure contains the output queue for the interface, its address, ...
696893Sfeldman  * We also have, for each interface, a UBA interface structure, which
706893Sfeldman  * contains information about the UNIBUS resources held by the interface:
716893Sfeldman  * map registers, buffered data paths, etc.  Information is cached in this
726893Sfeldman  * structure for use by the if_uba.c routines in running the interface
736893Sfeldman  * efficiently.
746893Sfeldman  */
7537476Ssklower 
7637476Ssklower struct ether_addr {
7737476Ssklower 	u_char	addr[6];
7837476Ssklower };
796893Sfeldman struct	il_softc {
8011575Ssam 	struct	arpcom is_ac;		/* Ethernet common part */
8111575Ssam #define	is_if	is_ac.ac_if		/* network-visible interface */
8211575Ssam #define	is_addr	is_ac.ac_enaddr		/* hardware Ethernet address */
836893Sfeldman 	struct	ifuba is_ifuba;		/* UNIBUS resources */
847261Ssam 	int	is_flags;
857261Ssam #define	ILF_RCVPENDING	0x2		/* start rcv in ilcint */
867261Ssam #define	ILF_STATPENDING	0x4		/* stat cmd pending */
8725444Skarels #define	ILF_RUNNING	0x8		/* board is running */
8825976Skarels #define	ILF_SETADDR	0x10		/* physical address is changed */
897261Ssam 	short	is_lastcmd;		/* can't read csr, so must save it */
907261Ssam 	short	is_scaninterval;	/* interval of stat collection */
917261Ssam #define	ILWATCHINTERVAL	60		/* once every 60 seconds */
9237476Ssklower 	union {
9337476Ssklower 	    struct	il_stats isu_stats;	/* holds on-board statistics */
9437476Ssklower 	    struct	ether_addr isu_maddrs[63];	/* multicast addrs */
9537476Ssklower 	}	is_isu;
9637476Ssklower #define is_stats	is_isu.isu_stats
9737476Ssklower #define is_maddrs	is_isu.isu_maddrs
987261Ssam 	struct	il_stats is_sum;	/* summation over time */
997261Ssam 	int	is_ubaddr;		/* mapping registers of is_stats */
1006893Sfeldman } il_softc[NIL];
1016893Sfeldman 
ilprobe(reg)1026893Sfeldman ilprobe(reg)
1036893Sfeldman 	caddr_t reg;
1046893Sfeldman {
1056893Sfeldman 	register int br, cvec;		/* r11, r10 value-result */
1066893Sfeldman 	register struct ildevice *addr = (struct ildevice *)reg;
1076893Sfeldman 	register i;
1086893Sfeldman 
1096893Sfeldman #ifdef lint
1106893Sfeldman 	br = 0; cvec = br; br = cvec;
1119179Ssam 	i = 0; ilrint(i); ilcint(i); ilwatch(i);
1126893Sfeldman #endif
1136893Sfeldman 
1146893Sfeldman 	addr->il_csr = ILC_OFFLINE|IL_CIE;
1156893Sfeldman 	DELAY(100000);
1167261Ssam 	i = addr->il_csr;		/* clear CDONE */
1176893Sfeldman 	if (cvec > 0 && cvec != 0x200)
1186893Sfeldman 		cvec -= 4;
1196893Sfeldman 	return (1);
1206893Sfeldman }
1216893Sfeldman 
1226893Sfeldman /*
1236893Sfeldman  * Interface exists: make available by filling in network interface
1246893Sfeldman  * record.  System will initialize the interface when it is ready
1256893Sfeldman  * to accept packets.  A STATUS command is done to get the ethernet
1266893Sfeldman  * address and other interesting data.
1276893Sfeldman  */
1286893Sfeldman ilattach(ui)
1296893Sfeldman 	struct uba_device *ui;
1306893Sfeldman {
1316893Sfeldman 	register struct il_softc *is = &il_softc[ui->ui_unit];
1327220Ssam 	register struct ifnet *ifp = &is->is_if;
1336893Sfeldman 	register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
1346893Sfeldman 
1357220Ssam 	ifp->if_unit = ui->ui_unit;
1367220Ssam 	ifp->if_name = "il";
1379746Ssam 	ifp->if_mtu = ETHERMTU;
13819865Skarels 	ifp->if_flags = IFF_BROADCAST;
1396893Sfeldman 
1406893Sfeldman 	/*
1417261Ssam 	 * Reset the board and map the statistics
1427261Ssam 	 * buffer onto the Unibus.
1436893Sfeldman 	 */
1447261Ssam 	addr->il_csr = ILC_RESET;
14525976Skarels 	(void)ilwait(ui, "reset");
1466893Sfeldman 
1479179Ssam 	is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats,
14813056Ssam 	    sizeof (struct il_stats), 0);
1497261Ssam 	addr->il_bar = is->is_ubaddr & 0xffff;
1507261Ssam 	addr->il_bcr = sizeof (struct il_stats);
1517261Ssam 	addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT;
15225976Skarels 	(void)ilwait(ui, "status");
1537261Ssam 	ubarelse(ui->ui_ubanum, &is->is_ubaddr);
15426167Skarels 	if (ildebug)
15526167Skarels 		printf("il%d: module=%s firmware=%s\n", ui->ui_unit,
15626167Skarels 			is->is_stats.ils_module, is->is_stats.ils_firmware);
15719865Skarels  	bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr,
15819865Skarels  	    sizeof (is->is_addr));
15925976Skarels 	printf("il%d: hardware address %s\n", ui->ui_unit,
16025976Skarels 		ether_sprintf(is->is_addr));
1617220Ssam 	ifp->if_init = ilinit;
16237476Ssklower 	ifp->if_output = ether_output;
16313056Ssam 	ifp->if_ioctl = ilioctl;
1648979Sroot 	ifp->if_reset = ilreset;
16537476Ssklower 	ifp->if_start = ilstart;
1666893Sfeldman 	is->is_ifuba.ifu_flags = UBA_CANTWAIT;
1677220Ssam 	if_attach(ifp);
1686893Sfeldman }
1696893Sfeldman 
17025976Skarels ilwait(ui, op)
17125976Skarels 	struct uba_device *ui;
17225976Skarels 	char *op;
17325976Skarels {
17425976Skarels 	register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
17525976Skarels 
17625976Skarels 	while ((addr->il_csr&IL_CDONE) == 0)
17725976Skarels 		;
17825976Skarels 	if (addr->il_csr&IL_STATUS) {
17925976Skarels 		printf("il%d: %s failed, csr=%b\n", ui->ui_unit, op,
18025976Skarels 			addr->il_csr, IL_BITS);
18125976Skarels 		return (-1);
18225976Skarels 	}
18325976Skarels 	return (0);
18425976Skarels }
18525976Skarels 
1866893Sfeldman /*
1876893Sfeldman  * Reset of interface after UNIBUS reset.
1886893Sfeldman  * If interface is on specified uba, reset its state.
1896893Sfeldman  */
ilreset(unit,uban)1906893Sfeldman ilreset(unit, uban)
1916893Sfeldman 	int unit, uban;
1926893Sfeldman {
1936893Sfeldman 	register struct uba_device *ui;
1946893Sfeldman 
1956893Sfeldman 	if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 ||
1966893Sfeldman 	    ui->ui_ubanum != uban)
1976893Sfeldman 		return;
1986893Sfeldman 	printf(" il%d", unit);
19925444Skarels 	il_softc[unit].is_if.if_flags &= ~IFF_RUNNING;
20025444Skarels 	il_softc[unit].is_flags &= ~ILF_RUNNING;
2016893Sfeldman 	ilinit(unit);
2026893Sfeldman }
2036893Sfeldman 
2046893Sfeldman /*
2056893Sfeldman  * Initialization of interface; clear recorded pending
2066893Sfeldman  * operations, and reinitialize UNIBUS usage.
2076893Sfeldman  */
ilinit(unit)2086893Sfeldman ilinit(unit)
2096893Sfeldman 	int unit;
2106893Sfeldman {
2116893Sfeldman 	register struct il_softc *is = &il_softc[unit];
2126893Sfeldman 	register struct uba_device *ui = ilinfo[unit];
2136893Sfeldman 	register struct ildevice *addr;
21413056Ssam 	register struct ifnet *ifp = &is->is_if;
2157261Ssam 	int s;
2166893Sfeldman 
21719865Skarels 	/* not yet, if address still unknown */
21819865Skarels 	if (ifp->if_addrlist == (struct ifaddr *)0)
21911575Ssam 		return;
22025444Skarels 	if (is->is_flags & ILF_RUNNING)
22125444Skarels 		return;
22211575Ssam 
22325444Skarels 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
22425444Skarels 		if (if_ubainit(&is->is_ifuba, ui->ui_ubanum,
22525444Skarels 		    sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) {
22625444Skarels 			printf("il%d: can't initialize\n", unit);
22725444Skarels 			is->is_if.if_flags &= ~IFF_UP;
22825444Skarels 			return;
22925444Skarels 		}
23037476Ssklower 		is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_isu,
23137476Ssklower 		    sizeof (is->is_isu), 0);
2326893Sfeldman 	}
23315071Skarels 	ifp->if_watchdog = ilwatch;
23415071Skarels 	is->is_scaninterval = ILWATCHINTERVAL;
23515071Skarels 	ifp->if_timer = is->is_scaninterval;
2366893Sfeldman 	addr = (struct ildevice *)ui->ui_addr;
2376893Sfeldman 
2386893Sfeldman 	/*
2399179Ssam 	 * Turn off source address insertion (it's faster this way),
24012488Ssam 	 * and set board online.  Former doesn't work if board is
24112488Ssam 	 * already online (happens on ubareset), so we put it offline
24212488Ssam 	 * first.
2439179Ssam 	 */
2449179Ssam 	s = splimp();
24525444Skarels 	addr->il_csr = ILC_RESET;
24625976Skarels 	if (ilwait(ui, "hardware diag")) {
24725444Skarels  		is->is_if.if_flags &= ~IFF_UP;
24825444Skarels  		splx(s);
24925444Skarels  		return;
25025444Skarels  	}
25112488Ssam 	addr->il_csr = ILC_CISA;
2529179Ssam 	while ((addr->il_csr & IL_CDONE) == 0)
2539179Ssam 		;
2549179Ssam 	/*
25526145Ssklower 	 * If we must reprogram this board's physical ethernet
25626145Ssklower 	 * address (as for secondary XNS interfaces), we do so
25726145Ssklower 	 * before putting it on line, and starting receive requests.
25826145Ssklower 	 * If you try this on an older 1010 board, it will total
25926145Ssklower 	 * wedge the board.
26026145Ssklower 	 */
26126145Ssklower 	if (is->is_flags & ILF_SETADDR) {
26237476Ssklower 		bcopy((caddr_t)is->is_addr, (caddr_t)&is->is_isu,
26326145Ssklower 							sizeof is->is_addr);
26426145Ssklower 		addr->il_bar = is->is_ubaddr & 0xffff;
26526145Ssklower 		addr->il_bcr = sizeof is->is_addr;
26626145Ssklower 		addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_LDPA;
26726145Ssklower 		if (ilwait(ui, "setaddr"))
26826145Ssklower 			return;
26926145Ssklower 		addr->il_bar = is->is_ubaddr & 0xffff;
27026145Ssklower 		addr->il_bcr = sizeof (struct il_stats);
27126145Ssklower 		addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT;
27226145Ssklower 		if (ilwait(ui, "verifying setaddr"))
27326145Ssklower 			return;
27426145Ssklower 		if (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr,
27526145Ssklower 						sizeof (is->is_addr)) != 0) {
27626145Ssklower 			printf("il%d: setaddr didn't work\n", ui->ui_unit);
27726145Ssklower 			return;
27826145Ssklower 		}
27926145Ssklower 	}
28037476Ssklower #ifdef MULTICAST
28137476Ssklower 	if (is->is_if.if_flags & IFF_PROMISC) {
28237476Ssklower 		addr->il_csr = ILC_PRMSC;
28337476Ssklower 		if (ilwait(ui, "all multi"))
28437476Ssklower 			return;
28537476Ssklower 	} else if (is->is_if.if_flags & IFF_ALLMULTI) {
28637476Ssklower 	too_many_multis:
28737476Ssklower 		addr->il_csr = ILC_ALLMC;
28837476Ssklower 		if (ilwait(ui, "all multi"))
28937476Ssklower 			return;
29037476Ssklower 	else {
29137476Ssklower 		int i;
29237476Ssklower 		register struct ether_addr *ep = is->is_maddrs;
29337476Ssklower 		struct ether_multi *enm;
29437476Ssklower 		struct ether_multistep step;
29537476Ssklower 		/*
29637476Ssklower 		 * Step through our list of multicast addresses.  If we have
29737476Ssklower 		 * too many multicast addresses, or if we have to listen to
29837476Ssklower 		 * a range of multicast addresses, turn on reception of all
29937476Ssklower 		 * multicasts.
30037476Ssklower 		 */
30137476Ssklower 		i = 0;
30237476Ssklower 		ETHER_FIRST_MULTI(step, &is->is_ac, enm);
30337476Ssklower 		while (enm != NULL) {
30437476Ssklower 			if (++i > 63 && k != 0) {
30537476Ssklower 				break;
30637476Ssklower 			}
30737476Ssklower 			*ep++ = *(struct ether_addr *)enm->enm_addrlo;
30837476Ssklower 			ETHER_NEXT_MULTI(step, enm);
30937476Ssklower 		}
31037476Ssklower 		if (i = 0) {
31137476Ssklower 			/* no multicasts! */
31237476Ssklower 		} else if (i <= 63) {
31337476Ssklower 			addr->il_bar = is->is_ubaddr & 0xffff;
31437476Ssklower 			addr->il_bcr = i * sizeof (struct ether_addr);
31537476Ssklower 			addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|
31637476Ssklower 						LC_LDGRPS;
31737476Ssklower 			if (ilwait(ui, "load multi"))
31837476Ssklower 				return;
31937476Ssklower 		} else {
32037476Ssklower 		    is->is_if.if_flags |= IFF_ALLMULTI;
32137476Ssklower 		    goto too_many_multis;
32237476Ssklower 		}
32337476Ssklower 	}
32437476Ssklower #endif MULTI
32526145Ssklower 	/*
3266893Sfeldman 	 * Set board online.
3276893Sfeldman 	 * Hang receive buffer and start any pending
3286893Sfeldman 	 * writes by faking a transmit complete.
32925444Skarels 	 * Receive bcr is not a multiple of 8 so buffer
3306893Sfeldman 	 * chaining can't happen.
3316893Sfeldman 	 */
3326893Sfeldman 	addr->il_csr = ILC_ONLINE;
3337220Ssam 	while ((addr->il_csr & IL_CDONE) == 0)
3347220Ssam 		;
3356893Sfeldman 	addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
3369746Ssam 	addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6;
3377261Ssam 	addr->il_csr =
33813056Ssam 	    ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE;
3397220Ssam 	while ((addr->il_csr & IL_CDONE) == 0)
3407220Ssam 		;
34138888Skarels 	is->is_if.if_flags |= IFF_RUNNING | IFF_OACTIVE;
34225444Skarels 	is->is_flags |= ILF_RUNNING;
3437261Ssam 	is->is_lastcmd = 0;
3446893Sfeldman 	ilcint(unit);
3456893Sfeldman 	splx(s);
3466893Sfeldman }
3476893Sfeldman 
3486893Sfeldman /*
3496893Sfeldman  * Start output on interface.
3506893Sfeldman  * Get another datagram to send off of the interface queue,
3516893Sfeldman  * and map it to the interface before starting the output.
3526893Sfeldman  */
35337476Ssklower ilstart(ifp)
35437476Ssklower 	register struct ifnet *ifp;
3556893Sfeldman {
35637476Ssklower         int unit = ifp->if_unit, len;
3576893Sfeldman 	struct uba_device *ui = ilinfo[unit];
3586893Sfeldman 	register struct il_softc *is = &il_softc[unit];
3596893Sfeldman 	register struct ildevice *addr;
3606893Sfeldman 	struct mbuf *m;
3617220Ssam 	short csr;
3626893Sfeldman 
3636893Sfeldman 	IF_DEQUEUE(&is->is_if.if_snd, m);
3647261Ssam 	addr = (struct ildevice *)ui->ui_addr;
3657261Ssam 	if (m == 0) {
3667261Ssam 		if ((is->is_flags & ILF_STATPENDING) == 0)
36737476Ssklower 			return (0);
3689179Ssam 		addr->il_bar = is->is_ubaddr & 0xffff;
3697261Ssam 		addr->il_bcr = sizeof (struct il_stats);
3707261Ssam 		csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE;
3717261Ssam 		is->is_flags &= ~ILF_STATPENDING;
3727261Ssam 		goto startcmd;
3737261Ssam 	}
3746893Sfeldman 	len = if_wubaput(&is->is_ifuba, m);
3759179Ssam 	/*
3769179Ssam 	 * Ensure minimum packet length.
3779179Ssam 	 * This makes the safe assumtion that there are no virtual holes
3789179Ssam 	 * after the data.
3799179Ssam 	 * For security, it might be wise to zero out the added bytes,
3809179Ssam 	 * but we're mainly interested in speed at the moment.
3819179Ssam 	 */
3829746Ssam 	if (len - sizeof(struct ether_header) < ETHERMIN)
3839746Ssam 		len = ETHERMIN + sizeof(struct ether_header);
3846893Sfeldman 	if (is->is_ifuba.ifu_flags & UBA_NEEDBDP)
3856893Sfeldman 		UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp);
3866893Sfeldman 	addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff;
3876893Sfeldman 	addr->il_bcr = len;
3887261Ssam 	csr =
3897261Ssam 	  ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE;
3907261Ssam 
3917261Ssam startcmd:
3927261Ssam 	is->is_lastcmd = csr & IL_CMD;
3937220Ssam 	addr->il_csr = csr;
39437476Ssklower 	is->is_if.if_flags |= IFF_OACTIVE;
39537476Ssklower 	return (0);
3966893Sfeldman }
3976893Sfeldman 
3986893Sfeldman /*
3996893Sfeldman  * Command done interrupt.
4006893Sfeldman  */
4016893Sfeldman ilcint(unit)
4026893Sfeldman 	int unit;
4036893Sfeldman {
4046893Sfeldman 	register struct il_softc *is = &il_softc[unit];
4057220Ssam 	struct uba_device *ui = ilinfo[unit];
4066893Sfeldman 	register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
4077266Ssam 	short csr;
4086893Sfeldman 
40937476Ssklower 	if ((is->is_if.if_flags & IFF_OACTIVE) == 0) {
4107220Ssam 		printf("il%d: stray xmit interrupt, csr=%b\n", unit,
4117261Ssam 			addr->il_csr, IL_BITS);
4126893Sfeldman 		return;
4136893Sfeldman 	}
4147220Ssam 
4157266Ssam 	csr = addr->il_csr;
4166893Sfeldman 	/*
4177261Ssam 	 * Hang receive buffer if it couldn't
4187261Ssam 	 * be done earlier (in ilrint).
4196893Sfeldman 	 */
4207261Ssam 	if (is->is_flags & ILF_RCVPENDING) {
42125444Skarels 		int s;
42225444Skarels 
4236893Sfeldman 		addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
4249746Ssam 		addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6;
4257261Ssam 		addr->il_csr =
4267261Ssam 		  ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE;
42725444Skarels 		s = splhigh();
4287220Ssam 		while ((addr->il_csr & IL_CDONE) == 0)
4297220Ssam 			;
43025444Skarels 		splx(s);
4317261Ssam 		is->is_flags &= ~ILF_RCVPENDING;
4326893Sfeldman 	}
43337476Ssklower 	is->is_if.if_flags &= ~IFF_OACTIVE;
4347266Ssam 	csr &= IL_STATUS;
4357261Ssam 	switch (is->is_lastcmd) {
4367261Ssam 
4377261Ssam 	case ILC_XMIT:
4387261Ssam 		is->is_if.if_opackets++;
4397266Ssam 		if (csr > ILERR_RETRIES)
4407261Ssam 			is->is_if.if_oerrors++;
4417261Ssam 		break;
4427261Ssam 
4437261Ssam 	case ILC_STAT:
4447266Ssam 		if (csr == ILERR_SUCCESS)
4457261Ssam 			iltotal(is);
4467261Ssam 		break;
4477261Ssam 	}
4486893Sfeldman 	if (is->is_ifuba.ifu_xtofree) {
4496893Sfeldman 		m_freem(is->is_ifuba.ifu_xtofree);
4506893Sfeldman 		is->is_ifuba.ifu_xtofree = 0;
4516893Sfeldman 	}
45237476Ssklower 	(void) ilstart(&is->is_if);
4536893Sfeldman }
4546893Sfeldman 
4556893Sfeldman /*
4566893Sfeldman  * Ethernet interface receiver interrupt.
4576893Sfeldman  * If input error just drop packet.
4586893Sfeldman  * Otherwise purge input buffered data path and examine
4596893Sfeldman  * packet to determine type.  If can't determine length
4606893Sfeldman  * from type, then have to drop packet.  Othewise decapsulate
4616893Sfeldman  * packet based on type and pass to type specific higher-level
4626893Sfeldman  * input routine.
4636893Sfeldman  */
4646893Sfeldman ilrint(unit)
4656893Sfeldman 	int unit;
4666893Sfeldman {
4676893Sfeldman 	register struct il_softc *is = &il_softc[unit];
4686893Sfeldman 	struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr;
4696893Sfeldman 	register struct il_rheader *il;
4706893Sfeldman     	struct mbuf *m;
47115787Sleres 	int len, off, resid, s;
4726893Sfeldman 	register struct ifqueue *inq;
4736893Sfeldman 
4746893Sfeldman 	is->is_if.if_ipackets++;
4756893Sfeldman 	if (is->is_ifuba.ifu_flags & UBA_NEEDBDP)
4766893Sfeldman 		UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp);
4776893Sfeldman 	il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr);
4786893Sfeldman 	len = il->ilr_length - sizeof(struct il_rheader);
4799746Ssam 	if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 ||
4809746Ssam 	    len > ETHERMTU) {
4816893Sfeldman 		is->is_if.if_ierrors++;
4826893Sfeldman #ifdef notdef
4836893Sfeldman 		if (is->is_if.if_ierrors % 100 == 0)
4846893Sfeldman 			printf("il%d: += 100 input errors\n", unit);
4856893Sfeldman #endif
4866893Sfeldman 		goto setup;
4876893Sfeldman 	}
4886893Sfeldman 
4896893Sfeldman 	/*
49019865Skarels 	 * Deal with trailer protocol: if type is trailer type
4916893Sfeldman 	 * get true type from first 16-bit word past data.
4926893Sfeldman 	 * Remember that type was trailer by setting off.
4936893Sfeldman 	 */
4949746Ssam 	il->ilr_type = ntohs((u_short)il->ilr_type);
4956893Sfeldman #define	ildataaddr(il, off, type)	((type)(((caddr_t)((il)+1)+(off))))
49619865Skarels 	if (il->ilr_type >= ETHERTYPE_TRAIL &&
49719865Skarels 	    il->ilr_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
49819865Skarels 		off = (il->ilr_type - ETHERTYPE_TRAIL) * 512;
4999746Ssam 		if (off >= ETHERMTU)
5006893Sfeldman 			goto setup;		/* sanity */
5019746Ssam 		il->ilr_type = ntohs(*ildataaddr(il, off, u_short *));
5029746Ssam 		resid = ntohs(*(ildataaddr(il, off+2, u_short *)));
5036893Sfeldman 		if (off + resid > len)
5046893Sfeldman 			goto setup;		/* sanity */
5056893Sfeldman 		len = off + resid;
5066893Sfeldman 	} else
5076893Sfeldman 		off = 0;
5086893Sfeldman 	if (len == 0)
5096893Sfeldman 		goto setup;
5106893Sfeldman 
5116893Sfeldman 	/*
5126893Sfeldman 	 * Pull packet off interface.  Off is nonzero if packet
5136893Sfeldman 	 * has trailing header; ilget will then force this header
5146893Sfeldman 	 * information to be at the front, but we still have to drop
5156893Sfeldman 	 * the type and length which are at the front of any trailer data.
5166893Sfeldman 	 */
51724791Skarels 	m = if_rubaget(&is->is_ifuba, len, off, &is->is_if);
51837476Ssklower 	if (m)
51937476Ssklower 		ether_input(&is->is_if, (struct ether_header *)il->ilr_dhost, m);
5206893Sfeldman setup:
5216893Sfeldman 	/*
5226893Sfeldman 	 * Reset for next packet if possible.
5236893Sfeldman 	 * If waiting for transmit command completion, set flag
5246893Sfeldman 	 * and wait until command completes.
5256893Sfeldman 	 */
52637476Ssklower 	if (is->is_if.if_flags & IFF_OACTIVE) {
5277261Ssam 		is->is_flags |= ILF_RCVPENDING;
5287220Ssam 		return;
5297220Ssam 	}
5307220Ssam 	addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
5319746Ssam 	addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6;
5327261Ssam 	addr->il_csr =
5337261Ssam 		((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE;
53425444Skarels 	s = splhigh();
5357220Ssam 	while ((addr->il_csr & IL_CDONE) == 0)
5367220Ssam 		;
53725444Skarels 	splx(s);
5386893Sfeldman }
5396893Sfeldman /*
5407261Ssam  * Watchdog routine, request statistics from board.
5417261Ssam  */
5427261Ssam ilwatch(unit)
5437261Ssam 	int unit;
5447261Ssam {
5457261Ssam 	register struct il_softc *is = &il_softc[unit];
5467261Ssam 	register struct ifnet *ifp = &is->is_if;
5477261Ssam 	int s;
5487261Ssam 
5497261Ssam 	if (is->is_flags & ILF_STATPENDING) {
5507261Ssam 		ifp->if_timer = is->is_scaninterval;
5517261Ssam 		return;
5527261Ssam 	}
5537261Ssam 	s = splimp();
5547261Ssam 	is->is_flags |= ILF_STATPENDING;
55537476Ssklower 	if ((is->is_if.if_flags & IFF_OACTIVE) == 0)
55637476Ssklower 		(void) ilstart(ifp);
5577261Ssam 	splx(s);
5587261Ssam 	ifp->if_timer = is->is_scaninterval;
5597261Ssam }
5607261Ssam 
5617261Ssam /*
5627261Ssam  * Total up the on-board statistics.
5637261Ssam  */
5647261Ssam iltotal(is)
5657261Ssam 	register struct il_softc *is;
5667261Ssam {
5677261Ssam 	register u_short *interval, *sum, *end;
5687261Ssam 
5697261Ssam 	interval = &is->is_stats.ils_frames;
5707261Ssam 	sum = &is->is_sum.ils_frames;
5717261Ssam 	end = is->is_sum.ils_fill2;
5727261Ssam 	while (sum < end)
5737261Ssam 		*sum++ += *interval++;
5747261Ssam 	is->is_if.if_collisions = is->is_sum.ils_collis;
57529730Ssklower 	if ((is->is_flags & ILF_SETADDR) &&
57629730Ssklower 	    (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr,
57729730Ssklower 					sizeof (is->is_addr)) != 0)) {
57829730Ssklower 		log(LOG_ERR, "il%d: physaddr reverted\n", is->is_if.if_unit);
57929730Ssklower 		is->is_flags &= ~ILF_RUNNING;
58029730Ssklower 		ilinit(is->is_if.if_unit);
58129730Ssklower 	}
5827261Ssam }
58313056Ssam 
58413056Ssam /*
58513056Ssam  * Process an ioctl request.
58613056Ssam  */
58713056Ssam ilioctl(ifp, cmd, data)
58813056Ssam 	register struct ifnet *ifp;
58913056Ssam 	int cmd;
59013056Ssam 	caddr_t data;
59113056Ssam {
59219865Skarels 	register struct ifaddr *ifa = (struct ifaddr *)data;
59325444Skarels 	register struct il_softc *is = &il_softc[ifp->if_unit];
59413056Ssam 	int s = splimp(), error = 0;
59513056Ssam 
59613056Ssam 	switch (cmd) {
59713056Ssam 
59813056Ssam 	case SIOCSIFADDR:
59919865Skarels 		ifp->if_flags |= IFF_UP;
60013056Ssam 		ilinit(ifp->if_unit);
60119865Skarels 
60237476Ssklower 		switch (ifa->ifa_addr->sa_family) {
60323557Ssklower #ifdef INET
60419865Skarels 		case AF_INET:
60519865Skarels 			((struct arpcom *)ifp)->ac_ipaddr =
60619865Skarels 				IA_SIN(ifa)->sin_addr;
60719865Skarels 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
60819865Skarels 			break;
60923557Ssklower #endif
61023557Ssklower #ifdef NS
61123557Ssklower 		case AF_NS:
61225976Skarels 		    {
61325976Skarels 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
61425976Skarels 
61525976Skarels 			if (ns_nullhost(*ina)) {
61625976Skarels 				ina->x_host = * (union ns_host *)
61725976Skarels 				     (il_softc[ifp->if_unit].is_addr);
61825976Skarels 			} else {
61925976Skarels 				il_setaddr(ina->x_host.c_host, ifp->if_unit);
62026145Ssklower 				return (0);
62125976Skarels 			}
62223557Ssklower 			break;
62325976Skarels 		    }
62423557Ssklower #endif
62519865Skarels 		}
62613056Ssam 		break;
62713056Ssam 
62825444Skarels 	case SIOCSIFFLAGS:
62925444Skarels 		if ((ifp->if_flags & IFF_UP) == 0 &&
63025444Skarels 		    is->is_flags & ILF_RUNNING) {
63125444Skarels 			((struct ildevice *)
63225444Skarels 			   (ilinfo[ifp->if_unit]->ui_addr))->il_csr = ILC_RESET;
63325444Skarels 			is->is_flags &= ~ILF_RUNNING;
63425444Skarels 		} else if (ifp->if_flags & IFF_UP &&
63525444Skarels 		    (is->is_flags & ILF_RUNNING) == 0)
63625444Skarels 			ilinit(ifp->if_unit);
63725444Skarels 		break;
63825444Skarels 
63913056Ssam 	default:
64013056Ssam 		error = EINVAL;
64113056Ssam 	}
64213056Ssam 	splx(s);
64313056Ssam 	return (error);
64413056Ssam }
64525976Skarels 
64625976Skarels /*
64725976Skarels  * set ethernet address for unit
64825976Skarels  */
64925976Skarels il_setaddr(physaddr, unit)
65025976Skarels u_char *physaddr;
65125976Skarels int unit;
65225976Skarels {
65325976Skarels 	register struct il_softc *is = &il_softc[unit];
65425976Skarels 
65525976Skarels 	if (! (is->is_flags & ILF_RUNNING))
65625976Skarels 		return;
65725976Skarels 
65826145Ssklower 	bcopy((caddr_t)physaddr, (caddr_t)is->is_addr, sizeof is->is_addr);
65926145Ssklower 	is->is_flags &= ~ILF_RUNNING;
66026145Ssklower 	is->is_flags |= ILF_SETADDR;
66126145Ssklower 	ilinit(unit);
66225976Skarels }
66325274Sbloom #endif
664