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