xref: /csrg-svn/sys/vax/if/if_pcl.c (revision 21774)
1*21774Skarels /*	if_pcl.c	6.3	85/06/03	*/
211816Ssam 
311816Ssam #include "pcl.h"
411816Ssam #if NPCL > 0
511816Ssam /*
611816Ssam  * DEC CSS PCL-11B Parallel Communications Interface
711816Ssam  *
813800Ssam  * Written by Mike Muuss and Jeff Schwab.
911816Ssam  */
1011816Ssam #include "../machine/pte.h"
1111816Ssam 
1217116Sbloom #include "param.h"
1317116Sbloom #include "systm.h"
1417116Sbloom #include "mbuf.h"
1517116Sbloom #include "buf.h"
1617116Sbloom #include "protosw.h"
1717116Sbloom #include "socket.h"
1817116Sbloom #include "vmmac.h"
1917116Sbloom #include "ioctl.h"
2017116Sbloom #include "errno.h"
2111816Ssam 
2211816Ssam #include "../net/if.h"
2311816Ssam #include "../net/netisr.h"
2411816Ssam #include "../net/route.h"
2511816Ssam #include "../netinet/in.h"
2611816Ssam #include "../netinet/in_systm.h"
2711816Ssam #include "../netinet/ip.h"
2811816Ssam #include "../netinet/ip_var.h"
2911816Ssam 
3011816Ssam #include "../vax/cpu.h"
3111816Ssam #include "../vax/mtpr.h"
3217116Sbloom #include "if_pclreg.h"
3317116Sbloom #include "if_uba.h"
3411816Ssam #include "../vaxuba/ubareg.h"
3511816Ssam #include "../vaxuba/ubavar.h"
3611816Ssam 
3711816Ssam /* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */
3813800Ssam #define	PCLMTU		(1006)	/* Max transmission unit (bytes) */
3913800Ssam #define	PCLMAXTDM	7	/* Max unit number on TDM bus */
4011816Ssam 
4111816Ssam int	pclprobe(), pclattach(), pclrint(), pclxint();
4213064Ssam int	pclinit(), pclioctl(), pcloutput(), pclreset();
4311816Ssam 
4411816Ssam struct	uba_device	*pclinfo[NPCL];
4511816Ssam u_short pclstd[] = { 0 };
4611816Ssam #define	PCLUNIT(x)	minor(x)
4711816Ssam struct	uba_driver pcldriver =
4811816Ssam 	{ pclprobe, 0, pclattach, 0, pclstd, "pcl", pclinfo };
4911816Ssam 
5011816Ssam /*
5111816Ssam  * PCL software status per interface.
5211816Ssam  *
5311816Ssam  * Each interface is referenced by a network interface structure,
5411816Ssam  * sc_if, which the routing code uses to locate the interface.
5511816Ssam  * This structure contains the output queue for the interface, its address, ...
5611816Ssam  * We also have, for each interface, a UBA interface structure, which
5711816Ssam  * contains information about the UNIBUS resources held by the interface:
5811816Ssam  * map registers, buffered data paths, etc.  Information is cached in this
5911816Ssam  * structure for use by the if_uba.c routines in running the interface
6011816Ssam  * efficiently.
6111816Ssam  */
6211816Ssam struct	pcl_softc {
6311816Ssam 	struct	ifnet sc_if;		/* network-visible interface */
6411816Ssam 	struct	ifuba sc_ifuba;		/* UNIBUS resources */
6511816Ssam 	short	sc_oactive;		/* is output active? */
6611816Ssam 	short	sc_olen;		/* length of last output */
6711816Ssam 	short	sc_lastdest;		/* previous destination */
6811816Ssam 	short	sc_odest;		/* current xmit destination */
6913800Ssam 	short	sc_bdest;		/* buffer's stated destination */
7011816Ssam 	short	sc_pattern;		/* identification pattern */
7111816Ssam } pcl_softc[NPCL];
7211816Ssam 
7311816Ssam /*
7411816Ssam  * Structure of "local header", which only goes between
7511816Ssam  * pcloutput and pclstart.
7611816Ssam  */
7711816Ssam struct pcl_header {
7811816Ssam 	short	pcl_dest;		/* Destination PCL station */
7911816Ssam };
8011816Ssam 
8111816Ssam /*
8211816Ssam  * Do non-DMA output of 1 word to determine presence of interface,
8311816Ssam  * and to find the interupt vector.  1 word messages are a special
8411816Ssam  * case in the receiver routine, and will be discarded.
8511816Ssam  */
8611816Ssam pclprobe(reg)
8711816Ssam 	caddr_t reg;
8811816Ssam {
8911816Ssam 	register int br, cvec;		/* r11, r10 value-result */
9011816Ssam 	register struct pcldevice *addr = (struct pcldevice *)reg;
9111816Ssam 
9211816Ssam #ifdef lint
9311816Ssam 	br = 0; cvec = br; br = cvec;
9411816Ssam 	pclrint(0); pclxint(0);
9511816Ssam #endif
9611816Ssam 	addr->pcl_rcr = PCL_RCINIT;
9711816Ssam 	addr->pcl_tcr = PCL_TXINIT;
9811816Ssam 	addr->pcl_tsba = 0xFFFE;
9911816Ssam 	/* going for 01777776 */
10011816Ssam 	addr->pcl_tsbc = -4;		/* really short */
10111816Ssam 	addr->pcl_tcr =
10211816Ssam 	 ((1 & 0xF) << 8) | PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 0x0030;
10311816Ssam 	DELAY(100000);
10411816Ssam 	addr->pcl_tcr = PCL_TXINIT;
10511816Ssam 	return (sizeof (struct pcldevice));
10611816Ssam }
10711816Ssam 
10811816Ssam /*
10911816Ssam  * Interface exists: make available by filling in network interface
11011816Ssam  * record.  System will initialize the interface when it is ready
11111816Ssam  * to accept packets.
11211816Ssam  */
11311816Ssam pclattach(ui)
11411816Ssam 	struct uba_device *ui;
11511816Ssam {
11611816Ssam 	register struct pcl_softc *sc = &pcl_softc[ui->ui_unit];
11711816Ssam 
11811816Ssam 	sc->sc_if.if_unit = ui->ui_unit;
11911816Ssam 	sc->sc_if.if_name = "pcl";
12011816Ssam 	sc->sc_if.if_mtu = PCLMTU;
12111816Ssam 	sc->sc_if.if_init = pclinit;
12211816Ssam 	sc->sc_if.if_output = pcloutput;
12313064Ssam 	sc->sc_if.if_ioctl = pclioctl;
12411816Ssam 	sc->sc_if.if_reset = pclreset;
125*21774Skarels 	sc->sc_if.if_flags = IFF_BROADCAST;
12611816Ssam 	sc->sc_ifuba.ifu_flags = UBA_NEEDBDP;
12711816Ssam 	if_attach(&sc->sc_if);
12811816Ssam }
12911816Ssam 
13011816Ssam /*
13111816Ssam  * Reset of interface after UNIBUS reset.
13211816Ssam  * If interface is on specified uba, reset its state.
13311816Ssam  */
13411816Ssam pclreset(unit, uban)
13511816Ssam 	int unit, uban;
13611816Ssam {
13711816Ssam 	register struct uba_device *ui;
13811816Ssam 
13911816Ssam 	if (unit >= NPCL || (ui = pclinfo[unit]) == 0 || ui->ui_alive == 0 ||
14011816Ssam 	    ui->ui_ubanum != uban)
14111816Ssam 		return;
14211816Ssam 	printf(" pcl%d", unit);
14311816Ssam 	pclinit(unit);
14411816Ssam }
14511816Ssam 
14611816Ssam /*
14711816Ssam  * Initialization of interface; clear recorded pending
14811816Ssam  * operations, and reinitialize UNIBUS usage.
14911816Ssam  */
15011816Ssam pclinit(unit)
15111816Ssam 	int unit;
15211816Ssam {
15311816Ssam 	register struct pcl_softc *sc = &pcl_softc[unit];
15411816Ssam 	register struct uba_device *ui = pclinfo[unit];
15511816Ssam 	register struct pcldevice *addr;
15611816Ssam 	int s;
15711816Ssam 
158*21774Skarels 	if (sc->sc_if.if_addrlist == (struct ifaddr *)0)
15913064Ssam 		return;
16011816Ssam 	if (if_ubainit(&sc->sc_ifuba, ui->ui_ubanum, 0,
16111816Ssam 	    (int)btoc(PCLMTU)) == 0) {
16211816Ssam 		printf("pcl%d: can't init\n", unit);
163*21774Skarels 		sc->sc_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
16411816Ssam 		return;
16511816Ssam 	}
166*21774Skarels 	sc->sc_if.if_flags |= IFF_RUNNING;
16711816Ssam 	addr = (struct pcldevice *)ui->ui_addr;
16811816Ssam 	addr->pcl_rcr = PCL_RCINIT;
16911816Ssam 	addr->pcl_tcr = PCL_TXINIT;
17011816Ssam 
17111816Ssam 	/*
17211816Ssam 	 * Hang a receive and start any
17311816Ssam 	 * pending writes by faking a transmit complete.
17411816Ssam 	 */
17511816Ssam 	s = splimp();
17611816Ssam 	addr->pcl_rdba = (short) sc->sc_ifuba.ifu_r.ifrw_info;
17711816Ssam 	addr->pcl_rdbc = -PCLMTU;
17811816Ssam 	addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_r.ifrw_info>>12))&0x0030) |
17911816Ssam 		PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
18011816Ssam 	sc->sc_oactive = 0;
18111816Ssam 	pclstart(unit);
18211816Ssam 	splx(s);
18311816Ssam }
18411816Ssam 
18511816Ssam /*
18611816Ssam  * PCL output routine.
18711816Ssam  */
18811816Ssam pcloutput(ifp, m, dst)
18911816Ssam 	struct ifnet *ifp;
19011816Ssam 	struct mbuf *m;
19111816Ssam 	struct sockaddr *dst;
19211816Ssam {
19313089Ssam 	int dest, s, error;
19411816Ssam 	struct pcl_header *pclp;
19511816Ssam 	struct mbuf *m2;
19611816Ssam 
19711816Ssam 	switch (dst->sa_family) {
19811816Ssam 
19911816Ssam #ifdef INET
20011816Ssam 	case AF_INET:
201*21774Skarels 		if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr))
202*21774Skarels 			dest = 0;
203*21774Skarels 		else
204*21774Skarels 			dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr);
20513800Ssam 		if (dest > PCLMAXTDM) {
20613800Ssam 			error = EHOSTUNREACH;
20713800Ssam 			goto bad;
20813800Ssam 		}
20911816Ssam 		break;
21011816Ssam #endif
21111816Ssam 	default:
21211816Ssam 		printf("pcl%d: can't handle af%d\n", ifp->if_unit,
21311816Ssam 			dst->sa_family);
21411816Ssam 		error = EAFNOSUPPORT;
21511816Ssam 		goto bad;
21611816Ssam 	}
21711816Ssam 
21811816Ssam 	/*
21911816Ssam 	 * Add pseudo local net header.
22011816Ssam 	 * Actually, it does not get transmitted, but merely stripped
22111816Ssam 	 * off and used by the START routine to route the packet.
22211816Ssam 	 * If no space in first mbuf, allocate another.
22311816Ssam 	 */
22411816Ssam 	if (m->m_off > MMAXOFF ||
22511816Ssam 	    MMINOFF + sizeof (struct pcl_header) > m->m_off) {
22611816Ssam 		m2 = m_get(M_DONTWAIT, MT_HEADER);
22711816Ssam 		if (m2 == 0) {
22811816Ssam 			error = ENOBUFS;
22911816Ssam 			goto bad;
23011816Ssam 		}
23111816Ssam 		m2->m_next = m;
23211816Ssam 		m2->m_off = MMINOFF;
23311816Ssam 		m2->m_len = sizeof (struct pcl_header);
23411816Ssam 		m = m2;
23511816Ssam 	} else {
23611816Ssam 		m->m_off -= sizeof (struct pcl_header);
23711816Ssam 		m->m_len += sizeof (struct pcl_header);
23811816Ssam 	}
23911816Ssam 	pclp = mtod(m, struct pcl_header *);
24011816Ssam 	pclp->pcl_dest = dest;
24111816Ssam 
24211816Ssam 	/*
24311816Ssam 	 * Queue message on interface, and start output if interface
24411816Ssam 	 * not yet active.
24511816Ssam 	 */
24611816Ssam 	s = splimp();
24711816Ssam 	if (IF_QFULL(&ifp->if_snd)) {
24811816Ssam 		IF_DROP(&ifp->if_snd);
24911816Ssam 		error = ENOBUFS;
25011816Ssam 		goto qfull;
25111816Ssam 	}
25211816Ssam 	IF_ENQUEUE(&ifp->if_snd, m);
25311816Ssam 	if (pcl_softc[ifp->if_unit].sc_oactive == 0)
25411816Ssam 		pclstart(ifp->if_unit);
25511816Ssam 	splx(s);
25611816Ssam 	return (0);
25711816Ssam qfull:
25811816Ssam 	splx(s);
25911816Ssam bad:
26011816Ssam 	m_freem(m);
26111816Ssam 	return (error);
26211816Ssam }
26311816Ssam 
26411816Ssam /*
26511816Ssam  * Start or restart output on interface.
26611816Ssam  * If interface is already active, then this is a retransmit.
26711816Ssam  * If interface is not already active, get another datagram
26811816Ssam  * to send off of the interface queue, and map it to the interface
26911816Ssam  * before starting the output.
27011816Ssam  */
27111816Ssam pclstart(dev)
27211816Ssam 	dev_t dev;
27311816Ssam {
27411816Ssam         int unit = PCLUNIT(dev);
27511816Ssam 	struct uba_device *ui = pclinfo[unit];
27611816Ssam 	register struct pcl_softc *sc = &pcl_softc[unit];
27711816Ssam 	register struct pcldevice *addr;
27811816Ssam 	struct mbuf *m;
27911816Ssam 
28011816Ssam 	if (sc->sc_oactive)
28111816Ssam 		goto restart;
28211816Ssam 
28311816Ssam 	/*
28411816Ssam 	 * Not already active: dequeue another request
28511816Ssam 	 * and map it to the UNIBUS.  If no more requests,
28611816Ssam 	 * just return.
28711816Ssam 	 */
28811816Ssam 	IF_DEQUEUE(&sc->sc_if.if_snd, m);
28911816Ssam 	if (m == 0) {
29011816Ssam 		sc->sc_oactive = 0;
29111816Ssam 		return;
29211816Ssam 	}
29311816Ssam 
29411816Ssam 	/*
29511816Ssam 	 * Pull destination node out of pseudo-local net header.
29611816Ssam 	 * remove it from outbound data.
29711816Ssam 	 * Note that if_wubaput calls m_bcopy, which is prepared for
29811816Ssam 	 * m_len to be 0 in the first mbuf in the chain.
29911816Ssam 	 */
30013800Ssam 	sc->sc_bdest = mtod(m, struct pcl_header *)->pcl_dest;
30113800Ssam 	sc->sc_odest = sc->sc_bdest? sc->sc_bdest: 1;
30211816Ssam 	m->m_off += sizeof (struct pcl_header);
30311816Ssam 	m->m_len -= sizeof (struct pcl_header);
30411816Ssam 
30511816Ssam 	/* Map out to the DMA area */
30611816Ssam 	sc->sc_olen = if_wubaput(&sc->sc_ifuba, m);
30711816Ssam 
30811816Ssam restart:
30911816Ssam 	/*
31011816Ssam 	 * Have request mapped to UNIBUS for transmission.
31113800Ssam 	 * Purge any stale data from this BDP, and start the output.
31211816Ssam 	 */
31311816Ssam 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
31411816Ssam 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp);
31511816Ssam 	addr = (struct pcldevice *)ui->ui_addr;
31611816Ssam 	addr->pcl_tcr = PCL_TXINIT;
31711816Ssam 	addr->pcl_tsba = (int)sc->sc_ifuba.ifu_w.ifrw_info;
31811816Ssam 	addr->pcl_tsbc = -sc->sc_olen;
31911816Ssam 
32011816Ssam 	/*
32111816Ssam 	 * RIB (retry if busy) is used on the second and subsequent packets
32211816Ssam 	 * to a single host, because TCP often wants to transmit multiple
32311816Ssam 	 * buffers in a row,
32411816Ssam 	 * and if they are all going to the same place, the second and
32511816Ssam 	 * subsequent ones may be lost due to receiver not ready again yet.
32611816Ssam 	 * This can cause serious problems, because the TCP will resend the
32711816Ssam 	 * whole window, which just repeats the problem.  The result is that
32811816Ssam 	 * a perfectly good link appears not to work unless we take steps here.
32911816Ssam 	 */
33011816Ssam 	addr->pcl_tcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
33111816Ssam 		((sc->sc_odest & 0xF)<<8) |
33211816Ssam 		PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE |
33311816Ssam 		(sc->sc_odest == sc->sc_lastdest ? PCL_RIB : 0);
33411816Ssam 	sc->sc_lastdest = sc->sc_odest;
33511816Ssam 	sc->sc_oactive = 1;
33611816Ssam }
33711816Ssam 
33811816Ssam /*
33911816Ssam  * PCL transmitter interrupt.
34011816Ssam  * Start another output if more data to send.
34111816Ssam  */
34211816Ssam pclxint(unit)
34311816Ssam 	int unit;
34411816Ssam {
34511816Ssam 	register struct uba_device *ui = pclinfo[unit];
34611816Ssam 	register struct pcl_softc *sc = &pcl_softc[unit];
34711816Ssam 	register struct pcldevice *addr = (struct pcldevice *)ui->ui_addr;
34811816Ssam 
34911816Ssam 	if (sc->sc_oactive == 0) {
35011816Ssam 		printf ("pcl%d: stray interrupt\n", unit);
35111816Ssam 		return;
35211816Ssam 	}
35311816Ssam 	if (addr->pcl_tsr & PCL_ERR) {
35411816Ssam 		sc->sc_lastdest = 0;		/* don't bother with RIB */
35511816Ssam 		if (addr->pcl_tsr & PCL_MSTDWN) {
35611816Ssam 			addr->pcl_tmmr = PCL_MASTER|PCL_AUTOADDR;
35711816Ssam 			pclstart(unit);	/* Retry */
35811816Ssam 			printf("pcl%d: master\n", unit );
35911816Ssam 			return;
36011816Ssam 		}
36113800Ssam #ifndef PCL_TESTING
36213800Ssam 		if ((addr->pcl_tsr & (PCL_ERR|PCL_RESPB)) == (PCL_ERR|0))  {
36313800Ssam 			;	/* Receiver Offline -- not exactly an error */
36413800Ssam 		}  else  {
36513800Ssam #else
36613800Ssam 		{
36713800Ssam #endif
36811816Ssam 			/* Log as an error */
36911816Ssam 			printf("pcl%d: send error, tcr=%b tsr=%b\n",
37011816Ssam 				unit, addr->pcl_tcr, PCL_TCSRBITS,
37111816Ssam 				addr->pcl_tsr, PCL_TERRBITS);
37211816Ssam 			sc->sc_if.if_oerrors++;
37311816Ssam 		}
37411816Ssam 	} else
37511816Ssam 		sc->sc_if.if_opackets++;
37613800Ssam 	if (sc->sc_bdest == 0 && sc->sc_odest < PCLMAXTDM) {
37713800Ssam 		sc->sc_odest++;		/* do next host (broadcast) */
37813800Ssam 	} else {
37913800Ssam 		sc->sc_oactive = 0;
38013800Ssam 		if (sc->sc_ifuba.ifu_xtofree) {
38113800Ssam 			m_freem(sc->sc_ifuba.ifu_xtofree);
38213800Ssam 			sc->sc_ifuba.ifu_xtofree = 0;
38313800Ssam 		}
38411816Ssam 	}
38511816Ssam 	pclstart(unit);
38611816Ssam }
38711816Ssam 
38811816Ssam /*
38911816Ssam  * PCL interface receiver interrupt.
39011816Ssam  * If input error just drop packet.
39111816Ssam  */
39211816Ssam pclrint(unit)
39311816Ssam 	int unit;
39411816Ssam {
39511816Ssam 	register struct pcl_softc *sc = &pcl_softc[unit];
39611816Ssam 	struct pcldevice *addr = (struct pcldevice *)pclinfo[unit]->ui_addr;
39711816Ssam     	struct mbuf *m;
39813089Ssam 	int len;
39911816Ssam 	register struct ifqueue *inq;
40011816Ssam 
40111816Ssam 	sc->sc_if.if_ipackets++;
40211816Ssam 	/*
40311816Ssam 	 * Purge BDP; drop if input error indicated.
40411816Ssam 	 */
40511816Ssam 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
40611816Ssam 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_r.ifrw_bdp);
40711816Ssam 	if (addr->pcl_rsr & PCL_ERR) {
40811816Ssam 		printf("pcl%d: rcv error, rcr=%b rsr=%b\n",
40911816Ssam 			unit, addr->pcl_rcr, PCL_RCSRBITS,
41011816Ssam 			addr->pcl_rsr, PCL_RERRBITS);
41111816Ssam 		sc->sc_if.if_ierrors++;
41211816Ssam 		goto setup;
41311816Ssam 	}
41411816Ssam 	len = PCLMTU + addr->pcl_rdbc;
41511816Ssam 	if (len <= 0 || len > PCLMTU) {
41611816Ssam 		printf("pcl%d: bad len=%d.\n", unit, len);
41711816Ssam 		sc->sc_if.if_ierrors++;
41811816Ssam 		goto setup;
41911816Ssam 	}
42011816Ssam 
42111816Ssam 	/* Really short packets will be part of the startup sequence */
42211816Ssam 	if (len <= 4) {
42311816Ssam 		/* Later, do comming-up processing here */
42411816Ssam 		goto setup;	/* drop packet */
42511816Ssam 	}
42611816Ssam 
42711816Ssam 	/*
42811816Ssam 	 * Pull packet off interface.
42911816Ssam 	 */
43011816Ssam 	m = if_rubaget(&sc->sc_ifuba, len, 0);
43111816Ssam 	if (m == 0)
43211816Ssam 		goto setup;
43311816Ssam 
43411816Ssam 	schednetisr(NETISR_IP);
43511816Ssam 	inq = &ipintrq;
43611816Ssam 
43711816Ssam 	if (IF_QFULL(inq)) {
43811816Ssam 		IF_DROP(inq);
43911816Ssam 		m_freem(m);
44011816Ssam 	} else
44111816Ssam 		IF_ENQUEUE(inq, m);
44211816Ssam setup:
44311816Ssam 	/*
44411816Ssam 	 * Reset for next packet.
44511816Ssam 	 */
44611816Ssam 	addr->pcl_rcr = PCL_RCINIT;
44711816Ssam 	addr->pcl_rdba = (int)sc->sc_ifuba.ifu_r.ifrw_info;
44811816Ssam 	addr->pcl_rdbc = -PCLMTU;
44911816Ssam 	addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
45011816Ssam 		PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
45111816Ssam }
45213064Ssam 
45313064Ssam /*
45413064Ssam  * Process an ioctl request.
45513064Ssam  */
45613064Ssam pclioctl(ifp, cmd, data)
45713064Ssam 	register struct ifnet *ifp;
45813064Ssam 	int cmd;
45913064Ssam 	caddr_t data;
46013064Ssam {
46113064Ssam 	int s = splimp(), error = 0;
46213064Ssam 
46313064Ssam 	switch (cmd) {
46413064Ssam 
46513064Ssam 	case SIOCSIFADDR:
466*21774Skarels 		if ((ifp->if_flags & IFF_RUNNING) == 0)
46713064Ssam 			pclinit(ifp->if_unit);
468*21774Skarels 		ifp->if_flags |= IFF_UP;
46913064Ssam 		break;
47013064Ssam 
47113064Ssam 	default:
47213064Ssam 		error = EINVAL;
47313064Ssam 	}
47413064Ssam 	splx(s);
47513064Ssam 	return (error);
47613064Ssam }
47711816Ssam #endif
478