xref: /original-bsd/sys/vax/if/if_dp.c (revision abb30312)
1 /*
2  * Copyright (c) 1990 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)if_dp.c	7.7 (Berkeley) 12/16/90
8  */
9 
10 #include "dp.h"
11 #if NDP > 0
12 
13 /*
14  * DPV-11 device driver, X.25 version
15  *
16  * Derived from dmc-11 driver:
17  *
18  *	Bill Nesheim
19  *	Cornell University
20  *
21  *	Lou Salkind
22  *	New York University
23  */
24 
25 /* #define DEBUG	/* for base table dump on fatal error */
26 
27 #include "../include/pte.h"
28 
29 #include "sys/param.h"
30 #include "sys/systm.h"
31 #include "sys/mbuf.h"
32 #include "sys/buf.h"
33 #include "sys/ioctl.h"		/* must precede tty.h */
34 #include "sys/protosw.h"
35 #include "sys/socket.h"
36 #include "sys/socketvar.h"
37 #include "sys/syslog.h"
38 #include "sys/vmmac.h"
39 #include "sys/errno.h"
40 #include "sys/time.h"
41 #include "sys/kernel.h"
42 
43 #include "net/if.h"
44 #include "net/netisr.h"
45 #include "net/route.h"
46 
47 #include "../include/cpu.h"
48 #include "../include/mtpr.h"
49 
50 #define	dzdevice dpdevice
51 #include "../uba/pdma.h"
52 #include "../uba/ubavar.h"
53 
54 #include "netccitt/x25.h"
55 #include "netccitt/pk.h"
56 #include "netccitt/pk_var.h"
57 
58 #include "if_dpreg.h"
59 
60 /*
61  * Driver information for auto-configuration stuff.
62  */
63 int	dpprobe(), dpattach(), dpinit(), dpioctl(), dprint(), dpxint();
64 int	dpoutput(), dpreset(), dptimeout(), dpstart(), x25_ifoutput(), dptestoutput();
65 
66 struct	uba_device *dpinfo[NDP];
67 
68 u_short	dpstd[] = { 0 };
69 struct	uba_driver dpdriver =
70 	{ dpprobe, 0, dpattach, 0, dpstd, "dp", dpinfo };
71 
72 /*
73  * Pdma structures for fast interrupts.
74  */
75 struct	pdma dppdma[2*NDP];
76 
77 /*
78  * DP software status per interface.
79  *
80  * Each interface is referenced by a network interface structure,
81  * dp_if, which the routing code uses to locate the interface.
82  * This structure contains the output queue for the interface, its address, ...
83  */
84 struct dp_softc {
85 	struct	ifnet dp_if;		/* network-visible interface */
86 	int	dp_ipl;
87 	struct	dpdevice *dp_addr;	/* dpcsr address */
88 	short	dp_iused;		/* input buffers given to DP */
89 	short	dp_flags;		/* flags */
90 #define DPF_RUNNING	0x01		/* device initialized */
91 #define DPF_ONLINE	0x02		/* device running (had a RDYO) */
92 #define DPF_RESTART	0x04		/* software restart in progress */
93 #define DPF_FLUSH	0x08		/* had a ROVR, flush ipkt when done */
94 	short	dp_ostate;		/* restarting, etc. */
95 	short	dp_istate;		/* not sure this is necessary */
96 #define DPS_IDLE	0
97 #define DPS_RESTART	1
98 #define DPS_ACTIVE	2
99 #define DPS_XEM		3		/* transmitting CRC, etc. */
100 	short	dp_olen;		/* length of last packet sent */
101 	short	dp_ilen;		/* length of last packet recvd */
102 	char	dp_obuf[DP_MTU+8];
103 	char	dp_ibuf[DP_MTU+8];
104 } dp_softc[NDP];
105 
106 /*
107  * Debug info
108  */
109 struct	dpstat {
110 	long	start;
111 	long	nohdr;
112 	long	init;
113 	long	rint;
114 	long	xint;
115 	long	reset;
116 	long	ioctl;
117 	long	down;
118 	long	mchange;
119 	long	timeout;
120 	long	rsm;
121 	long	rem;
122 	long	remchr;
123 	long	rga;
124 	long	xem;
125 	long	rovr;
126 } dpstat;
127 
128 short dp_ilb = 0;
129 short dp_log = 1;
130 
131 #define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
132 int dp_sizes[] = {
133 	sizeof(dp_softc[0]), sizeof(struct ifnet),
134 	_offsetof(struct dp_softc, dp_obuf[0]),
135 	_offsetof(struct dp_softc, dp_ibuf[0]),
136 	};
137 
138 dpprobe(reg, ui)
139 	caddr_t reg;
140 	struct	uba_device *ui;
141 {
142 	register int br, cvec;
143 	register struct dpdevice *addr = (struct dpdevice *)reg;
144 	register int unit = ui->ui_unit;
145 
146 #ifdef lint
147 	br = 0; cvec = br; br = cvec;
148 	dprint(0); dpxint(0);
149 #endif
150 	(void) spl6();
151 	addr->dpclr = DP_CLR;
152 	addr->dpclr = DP_XIE|DP_XE;
153 	DELAY(100000);
154 	dp_softc[unit].dp_ipl = br = qbgetpri();
155 	dp_softc[unit].dp_addr = addr;
156 	addr->dpclr = 0;
157 	if (cvec && cvec != 0x200){
158 		cvec -= 4;
159 	}
160 	return (1);
161 }
162 
163 /*
164  * Interface exists: make available by filling in network interface
165  * record.  System will initialize the interface when it is ready
166  * to accept packets.
167  */
168 dpattach(ui)
169 	register struct uba_device *ui;
170 {
171 	register struct dp_softc *dp = &dp_softc[ui->ui_unit];
172 
173 	dp->dp_if.if_unit = ui->ui_unit;
174 	dp->dp_if.if_name = "dp";
175 	dp->dp_if.if_mtu = DP_MTU;
176 	dp->dp_if.if_init = dpinit;
177 	dp->dp_if.if_output = x25_ifoutput;
178 	dp->dp_if.if_start = dpstart;
179 	dp->dp_if.if_ioctl = dpioctl;
180 	dp->dp_if.if_reset = dpreset;
181 	dp->dp_if.if_watchdog = dptimeout;
182 	dp->dp_if.if_flags = 0;
183 	if_attach(&dp->dp_if);
184 }
185 
186 /*
187  * Reset of interface after UNIBUS reset.
188  * If interface is on specified UBA, reset its state.
189  */
190 dpreset(unit, uban)
191 	int unit, uban;
192 {
193 	register struct uba_device *ui;
194 	register struct dp_softc *dp = &dp_softc[unit];
195 	register struct dpdevice *addr;
196 
197 	dpstat.reset++;
198 	if (unit >= NDP || (ui = dpinfo[unit]) == 0 || ui->ui_alive == 0 ||
199 	    ui->ui_ubanum != uban)
200 		return;
201 	dpdown(unit);
202 	dpinit(unit);
203 	printf(" dp%d", unit);
204 }
205 
206 /*
207  * Initialization of interface.
208  */
209 dpinit(unit)
210 	int unit;
211 {
212 	register struct dp_softc *dp = &dp_softc[unit];
213 	register struct dpdevice *addr;
214 	register struct ifaddr *ifa;
215 	register struct pdma *pdp = &dppdma[unit*2];
216 	int base, s;
217 
218 	dpstat.init++;
219 	/*
220 	 * Check to see that an address has been set.
221 	 */
222 	for (ifa = dp->dp_if.if_addrlist; ifa; ifa = ifa->ifa_next)
223 		if (ifa->ifa_addr->sa_family != AF_LINK)
224 			break;
225 	if (ifa == (struct ifaddr *) 0)
226 		return;
227 
228 	addr = dp->dp_addr;
229 	s = splimp();
230 	dp->dp_iused = 0;
231 	dp->dp_istate = dp->dp_ostate = DPS_IDLE;
232 	dp->dp_if.if_flags |= IFF_RUNNING;
233 	dp->dp_if.if_flags &= ~IFF_OACTIVE;
234 
235 	pdp->p_addr = addr;
236 	pdp->p_fcn = dpxint;
237 	pdp->p_mem = pdp->p_end = dp->dp_obuf;
238 	pdp++;
239 	pdp->p_addr = addr;
240 	pdp->p_fcn = dprint;
241 	/* force initial interrupt to come to dprint */
242 	pdp->p_mem = pdp->p_end = dp->dp_ibuf + DP_MTU + 8;
243 
244 	addr->dpclr = DP_CLR;
245 	DELAY(5000);
246 	/* DP_ATA = 0, DP_CHRM = 0, DP_SSLM = 1, (enable aborts),
247 			    CRC = CCIIT, initially all ones, 2nd addr = 0 */
248 	addr->dpsar = DP_SSLM | DP_IDLE;
249 	addr->dpclr = DP_XE | dp_ilb;
250 	addr->dptdsr = DP_XSM;
251 	/* enable receiver, receive interrupt, DTR, RTS */
252 	addr->dprcsr = DP_RIE | DP_MIE | DP_RE | DP_DTR | DP_RTS;
253 	dpstart(&dp->dp_if);
254 	splx(s);
255 }
256 
257 /*
258  * Start output on interface.  Get another datagram
259  * to send from the interface queue and map it to
260  * the interface before starting output.
261  *
262  */
263 dpstart(ifp)
264 	struct ifnet *ifp;
265 {
266 	int s, unit = ifp->if_unit, error = 0, len;
267 	register struct dp_softc *dp = &dp_softc[unit];
268 	register struct dpdevice *addr = dp->dp_addr;
269 	register struct mbuf *m;
270 	register char *cp;
271 	char *cplim;
272 
273 	/*
274 	 * If already doing output, go away and depend on transmit
275 	 * complete or error.
276 	 */
277 	dpstat.start++;
278 	if ((dp->dp_if.if_flags & IFF_OACTIVE) ||
279 	    (dp->dp_if.if_flags & IFF_RUNNING) == 0)
280 		goto out;
281 	IF_DEQUEUE(&dp->dp_if.if_snd, m);
282 	if (m == 0)
283 		goto out;
284 	dp->dp_if.if_collisions++;
285 	if (m->m_flags & M_PKTHDR)
286 		len = m->m_pkthdr.len;
287 	else {
288 		struct mbuf *m0 = m;
289 		for (len = 0; m; m = m->m_next)
290 			len += m->m_len;
291 		m = m0;
292 		dpstat.nohdr++;
293 	}
294 	if (len < 2)
295 		goto out;
296 	if (len > DP_MTU) {
297 		error = EINVAL;
298 		goto out;
299 	}
300 	dppdma[2*unit].p_mem = cp = dp->dp_obuf;
301 	while (m) {
302 		struct mbuf *n;
303 		bcopy(mtod(m, caddr_t), (caddr_t)cp, m->m_len);
304 		cp += m->m_len;
305 		MFREE(m, n); m = n;
306 	}
307 	dppdma[2*unit].p_end = cp - 1;
308 	dp->dp_if.if_flags |= IFF_OACTIVE;
309 	dp->dp_ostate = DPS_ACTIVE;
310 	dp->dp_if.if_collisions--;
311 	dp->dp_olen = len;
312 	if (dp_log) {
313 		register u_char *p = (u_char *)dp->dp_obuf;
314 		log(LOG_DEBUG, "dpoutput(%d):%x %x %x %x %x\n",
315 			len, p[0], p[1], p[2], p[3], p[4]);
316 	}
317 	addr->dpsar = DP_SSLM | DP_IDLE;
318 	addr->dprcsr = DP_RIE | DP_MIE | DP_RE | DP_DTR | DP_RTS;
319 	addr->dpclr = DP_XIE | DP_XE | dp_ilb;
320 	addr->dptdsr = DP_XSM;
321 out:
322 	return (error);
323 }
324 /*
325  * Receive done or error interrupt
326  */
327 dprint(unit, pdma, addr)
328 register struct pdma *pdma;
329 register struct dpdevice *addr;
330 {
331 	register struct dp_softc *dp = &dp_softc[unit];
332 	short rdsr = addr->dprdsr, rcsr = pdma->p_arg;
333 
334 	dpstat.rint++;
335 	splx(dp->dp_ipl);
336 	if (rdsr & DP_RGA) {
337 		/* DP_ATA = 0, DP_CHRM = 0, DP_SSLM = 1, (enable aborts),
338 			    CRC = CCIIT, initially all ones, 2nd addr = 0 */
339 		addr->dpsar = DP_SSLM | DP_IDLE;
340 		addr->dprcsr = DP_RIE | DP_MIE | DP_RE | DP_DTR | DP_RTS;
341 		dpstat.rga++;
342 		return;
343 	}
344 	if (rdsr & DP_RSM) { /* Received Start of Message */
345 		dpstat.rsm++;
346 		pdma->p_mem = dp->dp_ibuf;
347 		if (rcsr & DP_RDR) {
348 		    dp->dp_ibuf[0] = rdsr & DP_RBUF;
349 		    pdma->p_mem++;
350 		}
351 		dp->dp_flags &= ~DPF_FLUSH;
352 		return;
353 	}
354 	if (rdsr & DP_REM) { /* Received End of Message */
355 		dpstat.rem++;
356 		if (rcsr & DP_RDR) {
357 		    *(pdma->p_mem++) = rdsr;
358 		    dpstat.remchr++;
359 		}
360 		dp->dp_ilen = pdma->p_mem - dp->dp_ibuf;
361 		if (rdsr & DP_REC || dp->dp_flags & DPF_FLUSH) {
362 			dp->dp_if.if_ierrors++;
363 		} else
364 			dpinput(&dp->dp_if, dp->dp_ilen, dp->dp_ibuf);
365 		pdma->p_mem = pdma->p_end;
366 		dp->dp_flags &= ~ DPF_FLUSH;
367 		return;
368 	}
369 	if (rdsr & DP_ROVR) {
370 		dpstat.rovr++;
371 		dp->dp_flags |= DPF_FLUSH;
372 		return;
373 	}
374 	if (rcsr & DP_MSC) {
375 		dpstat.mchange++;
376 		if (0 == (rcsr & DP_DSR)) {
377 			log(LOG_DEBUG, "dp%d: lost modem\n", unit);
378 			/*dpdown(unit);*/
379 		}
380 		return;
381 	}
382 	dp->dp_flags |= DPF_FLUSH;
383 	if (pdma->p_mem != pdma->p_end)
384 		log(LOG_DEBUG, "dp%d: unexplained receiver interrupt\n", unit);
385 }
386 /*
387  * Transmit complete or error interrupt
388  */
389 dpxint(unit, pdma, addr)
390 register struct pdma *pdma;
391 register struct dpdevice *addr;
392 {
393 	register struct dp_softc *dp = &dp_softc[unit];
394 	int s;
395 
396 	splx(dp->dp_ipl);
397 	dpstat.xint++;
398 	if (addr->dptdsr & DP_XERR) {
399 		log(LOG_DEBUG, "if_dp%d: data late\n", unit);
400 	restart:
401 		pdma->p_mem = dp->dp_obuf;
402 		addr->dptdsr = DP_XSM;
403 		dp->dp_if.if_oerrors++;
404 		return;
405 	}
406 	switch (dp->dp_ostate) {
407 
408 	case DPS_ACTIVE:
409 		if (pdma->p_mem != pdma->p_end) {
410 			log(LOG_DEBUG, "if_dp%d: misc error in dpxint\n", unit);
411 			goto restart;
412 		}
413 		addr->dpsar = DP_IDLE|DP_SSLM;
414 		addr->dpclr = DP_XE | DP_XIE | dp_ilb;
415 		addr->dptdsr = DP_XEM | (0xff & pdma->p_mem[0]);
416 		addr->dprcsr = DP_RIE | DP_MIE | DP_RE | DP_DTR | DP_RTS;
417 		dp->dp_ostate = DPS_XEM;
418 		break;
419 
420 	case DPS_XEM:
421 		dpstat.xem++;
422 		dp->dp_if.if_opackets++;
423 		dp->dp_ostate = DPS_IDLE;
424 		dp->dp_if.if_flags &= ~IFF_OACTIVE;
425 		if (dp->dp_if.if_snd.ifq_len)
426 			dpstart(&dp->dp_if);
427 		else {
428 			addr->dpsar = DP_IDLE|DP_SSLM;
429 			addr->dpclr = DP_XE | dp_ilb;
430 			addr->dptdsr = DP_XSM;
431 			addr->dprcsr = DP_RIE | DP_MIE | DP_RE | DP_DTR|DP_RTS;
432 		}
433 		break;
434 
435 	default:
436 		log(LOG_DEBUG, "if_dp%d: impossible state in dpxint\n");
437 	}
438 }
439 /*
440  * Routine to copy from device buffer into mbufs.
441  *
442  * Warning: This makes the fairly safe assumption that
443  * mbufs have even lengths.
444  */
445 struct mbuf *
446 dpget(rxbuf, totlen, off, ifp)
447 	caddr_t rxbuf;
448 	int totlen, off;
449 	struct ifnet *ifp;
450 {
451 	register caddr_t cp;
452 	register struct mbuf *m;
453 	struct mbuf *top = 0, **mp = &top;
454 	int len;
455 	caddr_t packet_end;
456 
457 	cp = rxbuf;
458 	packet_end = cp + totlen;
459 	if (off) {
460 		off += 2 * sizeof(u_short);
461 		totlen -= 2 *sizeof(u_short);
462 		cp = rxbuf + off;
463 	}
464 
465 	MGETHDR(m, M_DONTWAIT, MT_DATA);
466 	if (m == 0)
467 		return (0);
468 	m->m_pkthdr.rcvif = ifp;
469 	m->m_pkthdr.len = totlen;
470 	m->m_len = MHLEN;
471 
472 	while (totlen > 0) {
473 		if (top) {
474 			MGET(m, M_DONTWAIT, MT_DATA);
475 			if (m == 0) {
476 				m_freem(top);
477 				return (0);
478 			}
479 			m->m_len = MLEN;
480 		}
481 		len = min(totlen, (packet_end - cp));
482 		if (len >= MINCLSIZE) {
483 			MCLGET(m, M_DONTWAIT);
484 			if (m->m_flags & M_EXT)
485 				m->m_len = len = min(len, MCLBYTES);
486 			else
487 				len = m->m_len;
488 		} else {
489 			/*
490 			 * Place initial small packet/header at end of mbuf.
491 			 */
492 			if (len < m->m_len) {
493 				if (top == 0 && len + max_linkhdr <= m->m_len)
494 					m->m_data += max_linkhdr;
495 				m->m_len = len;
496 			} else
497 				len = m->m_len;
498 		}
499 		bcopy(cp, mtod(m, caddr_t), (u_int)len);
500 		*mp = m;
501 		mp = &m->m_next;
502 		totlen -= len;
503 		cp += len;
504 		if (cp == packet_end)
505 			cp = rxbuf;
506 	}
507 	return (top);
508 }
509 
510 dpinput(ifp, len, buffer)
511 register struct ifnet *ifp;
512 caddr_t buffer;
513 {
514 	register struct ifqueue *inq;
515 	register struct mbuf *m;
516 	extern struct ifqueue hdintrq, ipintrq;
517 	int isr;
518 
519 	ifp->if_ipackets++;
520 	if (dp_log) {
521 		register u_char *p = (u_char *)buffer;
522 		log(LOG_DEBUG, "dpinput(%d):%x %x %x %x %x\n",
523 			len, p[0], p[1], p[2], p[3], p[4]);
524 	}
525 
526     {
527 	register struct ifaddr *ifa = ifp->if_addrlist;
528 	register u_char *cp = (u_char *)buffer;
529 
530 	for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
531 		if (ifa->ifa_addr->sa_family != AF_LINK)
532 			break;
533 	if (cp[0] == 0xff && cp[1] == 0x3) {
534 		/* This is a UI HDLC Packet, so we'll assume PPP
535 		   protocol.  for now, IP only. */
536 		buffer += 4;
537 		len -= 4;
538 		inq = &ipintrq;
539 		isr = NETISR_IP;
540 	} else {
541 		inq = &hdintrq;
542 		isr = NETISR_CCITT;
543 	}
544     }
545 	if (len <= 0)
546 		return;
547 
548 	m = dpget(buffer, len , 0, ifp);
549 	if (m == 0)
550 		return;
551 
552 	if(IF_QFULL(inq)) {
553 		IF_DROP(inq);
554 		m_freem(m);
555 	} else {
556 		IF_ENQUEUE(inq, m);
557 		schednetisr(isr);
558 	}
559 }
560 
561 /*
562  * Process an ioctl request.
563  */
564 dpioctl(ifp, cmd, data)
565 	register struct ifnet *ifp;
566 	int cmd;
567 	caddr_t data;
568 {
569 	register struct ifaddr *ifa = (struct ifaddr *)data;
570 	int s = splimp(), error = 0;
571 	struct dp_softc *dp = &dp_softc[ifp->if_unit];
572 
573 	dpstat.ioctl++;
574 	switch (cmd) {
575 
576 	case SIOCSIFADDR:
577 		ifp->if_flags |= IFF_UP;
578 		switch (ifa->ifa_addr->sa_family) {
579 		case AF_INET:
580 			ifp->if_output = dptestoutput;
581 		default:
582 			dpinit(ifp->if_unit);
583 			break;
584 		}
585 		break;
586 #ifdef CCITT
587 	case SIOCSIFCONF_X25:
588 		ifp->if_flags |= IFF_UP;
589 		ifp->if_output = x25_ifoutput;
590 		error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr);
591 		if (error == 0)
592 			dpinit(ifp->if_unit);
593 		break;
594 #endif
595 
596 	case SIOCSIFFLAGS:
597 		if ((ifp->if_flags & IFF_UP) == 0 &&
598 		    (ifp->if_flags & IFF_RUNNING))
599 			dpdown(ifp->if_unit);
600 		else if (ifp->if_flags & IFF_UP &&
601 		    (ifp->if_flags & IFF_RUNNING) == 0)
602 			dpinit(ifp->if_unit);
603 		break;
604 
605 
606 	default:
607 		error = EINVAL;
608 	}
609 	splx(s);
610 	return (error);
611 }
612 /*
613  * Reset a device and mark down.
614  * Flush output queue and drop queue limit.
615  */
616 dpdown(unit)
617 int unit;
618 {
619 
620 	register struct dp_softc *dp = &dp_softc[unit];
621 	register struct dpdevice *addr = dp->dp_addr;
622 
623 	dpstat.down++;
624 	if_qflush(&dp->dp_if.if_snd);
625 	dp->dp_flags = 0;
626 	dp->dp_if.if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
627 
628 	addr->dpclr = DP_CLR;
629 	DELAY(1000);
630 	addr->dpsar = 0;
631 	addr->dprcsr = 0;
632 }
633 
634 /*
635  * Watchdog timeout to see that transmitted packets don't
636  * lose interrupts.  The device has to be online (the first
637  * transmission may block until the other side comes up).
638  */
639 dptimeout(unit)
640 	int unit;
641 {
642 	register struct dp_softc *dp;
643 
644 	/* currently not armed */
645 	dpstat.timeout++;
646 	dp = &dp_softc[unit];
647 	if (dp->dp_if.if_flags & IFF_OACTIVE) {
648 		dpstart(&dp->dp_if);
649 	}
650 }
651 /*
652  * For debugging loopback activity.
653  */
654 static char pppheader[4] = { -1, 3, 0, 0x21 };
655 int dp_louts;
656 
657 dptestoutput(ifp, m, dst, rt)
658 register struct ifnet *ifp;
659 register struct mbuf *m;
660 struct sockaddr *dst;
661 struct rtentry *rt;
662 {
663 	/*
664 	 * Queue message on interface, and start output if interface
665 	 * not yet active.
666 	 */
667 	int s = splimp(), error = 0;
668 	dp_louts++;
669 	M_PREPEND(m, sizeof pppheader, M_DONTWAIT);
670 	if (m == 0) {
671 		splx(s);
672 		return ENOBUFS;
673 	}
674 	bcopy(pppheader, mtod(m, caddr_t), sizeof pppheader);
675 	if (IF_QFULL(&ifp->if_snd)) {
676 		IF_DROP(&ifp->if_snd);
677 	    /* printf("%s%d: HDLC says OK to send but queue full, may hang\n",
678 			ifp->if_name, ifp->if_unit);*/
679 		m_freem(m);
680 		error = ENOBUFS;
681 	} else {
682 		IF_ENQUEUE(&ifp->if_snd, m);
683 		if ((ifp->if_flags & IFF_OACTIVE) == 0)
684 			(*ifp->if_start)(ifp);
685 	}
686 	splx(s);
687 	return (error);
688 }
689 
690 #endif
691