xref: /csrg-svn/sys/deprecated/netimp/if_imp.c (revision 34853)
1 /*
2  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  *	@(#)if_imp.c	7.8 (Berkeley) 06/29/88
18  */
19 
20 #include "imp.h"
21 #if NIMP > 0
22 /*
23  * ARPANET IMP (PSN) interface driver.
24  *
25  * The IMP-host protocol (AHIP) is handled here, leaving
26  * hardware specifics to the lower level interface driver.
27  */
28 #include "param.h"
29 #include "systm.h"
30 #include "mbuf.h"
31 #include "buf.h"
32 #include "protosw.h"
33 #include "socket.h"
34 #include "time.h"
35 #include "kernel.h"
36 #include "errno.h"
37 #include "ioctl.h"
38 #include "syslog.h"
39 
40 #include "../machine/mtpr.h"
41 
42 #include "../net/if.h"
43 #include "../net/netisr.h"
44 #include "../netinet/in.h"
45 #include "../netinet/in_systm.h"
46 #include "../netinet/in_var.h"
47 #include "../netinet/ip.h"
48 #include "../netinet/ip_var.h"
49 #define IMPMESSAGES
50 /* define IMPLEADERS here to get leader printing code */
51 #include "if_imp.h"
52 #include "if_imphost.h"
53 
54 struct	imp_softc imp_softc[NIMP];
55 #ifndef lint
56 int	nimp = NIMP;			/* for netstat */
57 #endif
58 struct	ifqueue impintrq;
59 int	impqmaxlen = IFQ_MAXLEN;
60 int	imphqlen = 12 + IMP_MAXHOSTMSG;	/* max packets to queue per host */
61 
62 int	imppri = LOG_ERR;
63 #ifdef IMPLEADERS
64 int	impprintfs = 0;
65 #endif
66 #ifdef IMPINIT
67 int	imptraceinit = 0;
68 #endif
69 
70 
71 #define HOSTDEADTIMER	(30 * PR_SLOWHZ)	/* How long to wait when down */
72 
73 int	impdown(), impinit(), impioctl(), impoutput(), imptimo();
74 
75 /*
76  * IMP attach routine.  Called from hardware device attach routine
77  * at configuration time with a pointer to the device structure.
78  * Sets up local state and returns pointer to base of ifnet+impcb
79  * structures.  This is then used by the device's attach routine
80  * set up its back pointers.
81  */
82 struct imp_softc *
83 impattach(hwname, hwunit, reset)
84 	char *hwname;
85 	int hwunit;
86 	int (*reset)();
87 {
88 	struct imp_softc *sc;
89 	register struct ifnet *ifp;
90 	static int impunit;
91 
92 #ifdef lint
93 	impintr();
94 #endif
95 	if (impunit >= NIMP) {
96 		printf("imp%d: not configured\n", impunit++);
97 		return (0);
98 	}
99 	sc = &imp_softc[impunit];
100 	ifp = &sc->imp_if;
101 	sc->imp_cb.ic_hwname = hwname;
102 	sc->imp_cb.ic_hwunit = hwunit;
103 	ifp->if_unit = impunit;
104 	ifp->if_name = "imp";
105 	ifp->if_mtu = IMPMTU - sizeof(struct imp_leader);
106 	ifp->if_reset = reset;
107 	ifp->if_init = impinit;
108 	ifp->if_ioctl = impioctl;
109 	ifp->if_output = impoutput;
110 	ifp->if_watchdog = imptimo;
111 	if_attach(ifp);
112 	impunit++;
113 	return (sc);
114 }
115 
116 /*
117  * IMP initialization routine: call hardware module to
118  * setup resources, init state and get ready for
119  * NOOPs the IMP should send us, and that we want to drop.
120  */
121 impinit(unit)
122 	int unit;
123 {
124 	int s;
125 	register struct imp_softc *sc = &imp_softc[unit];
126 
127 	if (sc->imp_if.if_addrlist == 0)
128 		return;
129 	s = splimp();
130 #ifdef IMPINIT
131 	if (imptraceinit)
132 		log(imppri, "impinit\n");
133 #endif
134 	sc->imp_state = IMPS_WINIT;
135 	if ((*sc->imp_cb.ic_init)(sc->imp_cb.ic_hwunit) == 0)
136 		sc->imp_if.if_flags &= ~IFF_UP;
137 	impintrq.ifq_maxlen = impqmaxlen;
138 	splx(s);
139 }
140 
141 /*
142  * ARPAnet 1822/AHIP input routine.
143  * Called from hardware input interrupt routine to handle 1822
144  * IMP-host messages.  Data messages are passed to higher-level
145  * protocol processors on the basis of link number.
146  * Other type messages (control) are handled here.
147  */
148 impinput(unit, m)
149 	int unit;
150 	register struct mbuf *m;
151 {
152 	register struct control_leader *cp;
153 #define	ip	((struct imp_leader *)cp)
154 	register struct imp_softc *sc = &imp_softc[unit];
155 	struct ifnet *ifp;
156 	register struct host *hp;
157 	register struct ifqueue *inq;
158 	struct sockaddr_in *sin;
159 	int s;
160 
161 	/*
162 	 * Pull the interface pointer out of the mbuf
163 	 * and save for later; adjust mbuf to look at rest of data.
164 	 */
165 	ifp = *(mtod(m, struct ifnet **));
166 	IF_ADJ(m);
167 	/* verify leader length. */
168 	if (m->m_len < sizeof(struct control_leader) &&
169 	    (m = m_pullup(m, sizeof(struct control_leader))) == 0)
170 		return;
171 	cp = mtod(m, struct control_leader *);
172 	if (cp->dl_mtype == IMPTYPE_DATA &&
173 	    m->m_len < sizeof(struct imp_leader)) {
174 		if ((m = m_pullup(m, sizeof(struct imp_leader))) == 0)
175 			return;
176 		cp = mtod(m, struct control_leader *);
177 	}
178 #ifdef IMPLEADERS
179 	if (impprintfs)
180 		printleader("impinput", ip);
181 #endif
182 	inq = &impintrq;
183 
184 	/* check leader type */
185 	if (cp->dl_format != IMP_NFF) {
186 		/*
187 		 * We get 1822L NOOPs and RESET
188 		 * at initialization.
189 		 */
190 #ifdef IMPINIT
191 		if (imptraceinit)
192 			log(imppri, "input, format %x mtype %d\n",
193 			    cp->dl_format, cp->dl_mtype);
194 #endif
195 		if (cp->dl_format != IMP_1822L_I2H ||
196 		    (cp->dl_mtype != IMPTYPE_NOOP &&
197 		    cp->dl_mtype != IMPTYPE_RESET)) {
198 			sc->imp_garbage++;
199 			sc->imp_if.if_collisions++;	/* XXX */
200 		}
201 	} else switch (cp->dl_mtype) {
202 
203 	case IMPTYPE_DATA:
204 		/*
205 		 * Data for a protocol.  Dispatch to the appropriate
206 		 * protocol routine (running at software interrupt).
207 		 * If this isn't a raw interface, advance pointer
208 		 * into mbuf past leader.
209 		 */
210 		switch (cp->dl_link) {
211 
212 		case IMPLINK_IP:
213 			m->m_len -= sizeof(struct imp_leader);
214 			m->m_off += sizeof(struct imp_leader);
215 			schednetisr(NETISR_IP);
216 			inq = &ipintrq;
217 			break;
218 
219 		default:
220 			break;
221 		}
222 		break;
223 
224 	/*
225 	 * IMP leader error.  Reset the IMP and discard the packet.
226 	 */
227 	case IMPTYPE_BADLEADER:
228 		/*
229 		 * According to 1822 document, this message
230 		 * will be generated in response to the
231 		 * first noop sent to the IMP after
232 		 * the host resets the IMP interface.
233 		 */
234 #ifdef IMPINIT
235 		if (imptraceinit)
236 			log(imppri, "badleader\n");
237 #endif
238 		if (sc->imp_state != IMPS_INIT) {
239 			impmsg(sc, "leader error");
240 			sc->imp_msgready = 0;
241 			hostreset(unit);
242 			impnoops(sc);
243 			sc->imp_garbage++;
244 		}
245 		break;
246 
247 	/*
248 	 * IMP going down.  Print message, and if not immediate,
249 	 * set off a timer to insure things will be reset at the
250 	 * appropriate time.
251 	 */
252 	case IMPTYPE_DOWN:
253 	    {	int type, when;
254 
255 		type = cp->dl_link & IMP_DMASK;
256 		when = (cp->dl_link & IMPDOWN_WHENMASK) >> IMPDOWN_WHENSHIFT;
257 #ifdef IMPINIT
258 		if (imptraceinit)
259 			log(imppri, "input DOWN %s %d\n",
260 			    impmessage[type], when * IMPDOWN_WHENUNIT);
261 #endif
262 		if (type != IMPDOWN_GOING && when)
263 			impmsg(sc, "going down %s in %d minutes",
264 			    (u_int)impmessage[type], when * IMPDOWN_WHENUNIT);
265 		else
266 			impmsg(sc, "going down %s", (u_int)impmessage[type]);
267 		if (sc->imp_state != IMPS_UP)
268 			break;
269 		if (type == IMPDOWN_GOING) {
270 			sc->imp_state = IMPS_GOINGDOWN;
271 			timeout(impdown, (caddr_t)sc, IMPTV_DOWN * hz);
272 		} else if (when == 0)
273 			sc->imp_state = IMPS_WINIT;
274 		sc->imp_dropcnt = 0;
275 		break;
276 	    }
277 
278 	/*
279 	 * A NOP, usually seen during the initialization sequence.
280 	 * Compare the local address with that in the message.
281 	 * Reset the local address notion if it doesn't match.
282 	 */
283 	case IMPTYPE_NOOP:
284 #ifdef IMPINIT
285 		if (imptraceinit)
286 			log(imppri, "noop\n");
287 #endif
288 		if (sc->imp_state == IMPS_WINIT) {
289 			sc->imp_dropcnt = 0;
290 			impnoops(sc);
291 			sc->imp_state = IMPS_INIT;
292 		}
293 		sc->imp_dropcnt++;
294 		if (sc->imp_state == IMPS_INIT && cp->dl_imp != 0) {
295 			struct in_addr leader_addr;
296 
297 			sin = (struct sockaddr_in *)&sc->imp_if.if_addrlist->ifa_addr;
298 			imp_leader_to_addr(&leader_addr, cp, &sc->imp_if);
299 			if (sin->sin_addr.s_addr != leader_addr.s_addr) {
300 				impmsg(sc, "address reset to x%x (%d/%d)",
301 					ntohl(leader_addr.s_addr),
302 					(u_int)cp->dl_host,
303 					ntohs(cp->dl_imp));
304 				sin->sin_addr.s_addr = leader_addr.s_addr;
305 			}
306 		}
307 		break;
308 
309 	/*
310 	 * RFNM or INCOMPLETE message, decrement rfnm count
311 	 * and prepare to send next message.
312 	 * If the rfnm allows another queued
313 	 * message to be sent, bump msgready
314 	 * and start IMP if idle.
315 	 * We could pass incomplete's up to the next level,
316 	 * but this currently isn't needed.
317 	 * Pass "bad" incompletes and rfnms to the raw socket.
318 	 */
319 	case IMPTYPE_INCOMPLETE:
320 		sc->imp_incomplete++;
321 		/* FALL THROUGH */
322 	case IMPTYPE_RFNM:
323 		if ((hp = hostlookup((int)cp->dl_imp, (int)cp->dl_host,
324 		    unit)) == 0 || hp->h_rfnm == 0) {
325 			sc->imp_badrfnm++;
326 			if (hp)
327 				hostfree(hp);
328 			break;
329 		}
330 		imprestarthost(sc, hp);
331 		if (cp->dl_mtype == IMPTYPE_RFNM)
332 			goto drop;
333 		break;
334 
335 	/*
336 	 * Host or IMP can't be reached.  Flush any packets
337 	 * awaiting transmission and release the host structure.
338 	 * Enqueue for notifying protocols at software interrupt time.
339 	 */
340 	case IMPTYPE_HOSTDEAD:
341 	case IMPTYPE_HOSTUNREACH:
342 		if (hp = hostlookup((int)cp->dl_imp, (int)cp->dl_host, unit)) {
343 			hp->h_flags |= (1 << (int)cp->dl_mtype);
344 			sc->imp_msgready -=
345 			   MIN(hp->h_qcnt, IMP_MAXHOSTMSG - hp->h_rfnm);
346 			hp->h_rfnm = 0;
347 			hostflush(hp);
348 			hp->h_timer = HOSTDEADTIMER;
349 		}
350 		break;
351 
352 	/*
353 	 * Error in data.  Clear RFNM status for this host and send
354 	 * noops to the IMP to clear the interface.
355 	 */
356 	case IMPTYPE_BADDATA:
357 		impmsg(sc, "data error");
358 		if (hp = hostlookup((int)cp->dl_imp, (int)cp->dl_host, unit)) {
359 			sc->imp_msgready -=
360 			   MIN(hp->h_qcnt, IMP_MAXHOSTMSG - hp->h_rfnm);
361 			if (hp->h_rfnm)
362 				hostrelease(hp);
363 			else
364 				hostfree(hp);
365 		}
366 		impnoops(sc);
367 		break;
368 
369 	/*
370 	 * Interface reset.
371 	 */
372 	case IMPTYPE_RESET:
373 #ifdef IMPINIT
374 		if (imptraceinit)
375 			log(imppri, "reset complete\n");
376 #endif
377 		if (sc->imp_state != IMPS_INIT) {
378 			impmsg(sc, "interface reset");
379 			impnoops(sc);
380 		}
381 		/* clear RFNM counts */
382 		sc->imp_msgready = 0;
383 		hostreset(unit);
384 		if (sc->imp_state != IMPS_DOWN) {
385 			sc->imp_state = IMPS_UP;
386 			sc->imp_if.if_flags |= IFF_UP;
387 #ifdef IMPINIT
388 			if (imptraceinit)
389 				log(imppri, "IMP UP\n");
390 #endif
391 		}
392 		break;
393 
394 	default:
395 		sc->imp_garbage++;
396 		sc->imp_if.if_collisions++;		/* XXX */
397 		break;
398 	}
399 
400 	if (inq == &impintrq)
401 		schednetisr(NETISR_IMP);
402 	/*
403 	 * Re-insert interface pointer in the mbuf chain
404 	 * for the next protocol up.
405 	 */
406 	if (M_HASCL(m) && (mtod(m, int) & CLOFSET) < sizeof(struct ifnet *)) {
407 		struct mbuf *n;
408 
409 		MGET(n, M_DONTWAIT, MT_HEADER);
410 		if (n == 0)
411 			goto drop;
412 		n->m_next = m;
413 		m = n;
414 		m->m_len = 0;
415 		m->m_off = MMINOFF + sizeof(struct ifnet  *);
416 	}
417 	m->m_off -= sizeof(struct ifnet *);
418 	m->m_len += sizeof(struct ifnet *);
419 	*(mtod(m, struct ifnet **)) = ifp;
420 
421 	s = splimp();
422 	if (!IF_QFULL(inq)) {
423 		IF_ENQUEUE(inq, m);
424 		splx(s);
425 		return;
426 	}
427 	splx(s);
428 	IF_DROP(inq);
429 drop:
430 	m_freem(m);
431 #undef ip
432 }
433 
434 /*
435  * Bring the IMP down after notification.
436  */
437 impdown(sc)
438 	struct imp_softc *sc;
439 {
440 	int s = splimp();
441 
442 	if (sc->imp_state == IMPS_GOINGDOWN) {
443 		sc->imp_state = IMPS_WINIT;
444 		impmsg(sc, "marked down");
445 		sc->imp_msgready = 0;
446 		hostreset(sc->imp_if.if_unit);
447 		if_down(&sc->imp_if);
448 	}
449 #ifdef IMPINIT
450 	else if (imptraceinit)
451 		log(imppri, "impdown, state now %d (ignored)\n", sc->imp_state);
452 #endif
453 	splx(s);
454 }
455 
456 /*VARARGS2*/
457 impmsg(sc, fmt, a1)
458 	struct imp_softc *sc;
459 	char *fmt;
460 	u_int a1;
461 {
462 
463 	log(imppri, "imp%d: %r\n", sc->imp_if.if_unit, fmt, &a1);
464 }
465 
466 struct sockproto impproto = { PF_IMPLINK };
467 struct sockaddr_in impdst = { AF_IMPLINK };
468 struct sockaddr_in impsrc = { AF_IMPLINK };
469 
470 /*
471  * Pick up the IMP "error" messages enqueued earlier,
472  * passing these up to the higher level protocol
473  * and the raw interface.
474  */
475 impintr()
476 {
477 	register struct mbuf *m;
478 	register struct control_leader *cp;
479 	struct ifnet *ifp;
480 	int s;
481 
482 	for (;;) {
483 		s = splimp();
484 		IF_DEQUEUEIF(&impintrq, m, ifp);
485 		splx(s);
486 		if (m == 0)
487 			return;
488 
489 		cp = mtod(m, struct control_leader *);
490 		imp_leader_to_addr(&impsrc.sin_addr, cp, ifp);
491 		impproto.sp_protocol = cp->dl_link;
492 		impdst.sin_addr = IA_SIN(ifp->if_addrlist)->sin_addr;
493 
494 		if (cp->dl_mtype == IMPTYPE_HOSTDEAD ||
495 		    cp->dl_mtype == IMPTYPE_HOSTUNREACH)
496 			switch (cp->dl_link) {
497 
498 			case IMPLINK_IP:
499 				pfctlinput((int)cp->dl_mtype,
500 				    (struct sockaddr *)&impsrc);
501 				break;
502 			default:
503 				raw_ctlinput((int)cp->dl_mtype,
504 				    (struct sockaddr *)&impsrc);
505 				break;
506 			}
507 
508 		raw_input(m, &impproto, (struct sockaddr *)&impsrc,
509 		  (struct sockaddr *)&impdst);
510 	}
511 }
512 
513 /*
514  * ARPAnet 1822 output routine.
515  * Called from higher level protocol routines to set up messages for
516  * transmission to the imp.  Sets up the header and calls impsnd to
517  * enqueue the message for this IMP's hardware driver.
518  */
519 impoutput(ifp, m0, dst)
520 	register struct ifnet *ifp;
521 	struct mbuf *m0;
522 	struct sockaddr *dst;
523 {
524 	register struct imp_leader *imp;
525 	register struct mbuf *m = m0;
526 	caddr_t pkt = mtod(m, caddr_t);
527 	int error = 0;
528 
529 	/*
530 	 * Don't even try if the IMP is unavailable.
531 	 */
532 	if (!IMPS_RUNNING(imp_softc[ifp->if_unit].imp_state)) {
533 		error = ENETDOWN;
534 		goto drop;
535 	}
536 
537 	/*
538 	 * If AF_IMPLINK, leader exists; just send.
539 	 * Otherwise, construct leader according to address family.
540 	 */
541 	if (dst->sa_family != AF_IMPLINK) {
542 		/*
543 		 * Add IMP leader.  If there's not enough space in the
544 		 * first mbuf, allocate another.  If that should fail, we
545 		 * drop this sucker.
546 		 */
547 		if (m->m_off > MMAXOFF ||
548 		    MMINOFF + sizeof(struct imp_leader) > m->m_off) {
549 			MGET(m, M_DONTWAIT, MT_HEADER);
550 			if (m == 0) {
551 				error = ENOBUFS;
552 				goto drop;
553 			}
554 			m->m_next = m0;
555 			m->m_len = sizeof(struct imp_leader);
556 		} else {
557 			m->m_off -= sizeof(struct imp_leader);
558 			m->m_len += sizeof(struct imp_leader);
559 		}
560 		imp = mtod(m, struct imp_leader *);
561 		imp->il_format = IMP_NFF;
562 		imp->il_mtype = IMPTYPE_DATA;
563 		imp->il_flags = 0;
564 		imp->il_htype = 0;
565 		imp->il_subtype = 0;
566 
567 		switch (dst->sa_family) {
568 
569 		case AF_INET:
570 			imp->il_link = IMPLINK_IP;
571 			imp_addr_to_leader((struct control_leader *)imp,
572 				((struct sockaddr_in *)dst)->sin_addr.s_addr);
573 			imp->il_length = htons(ntohs((u_short)
574 			    ((struct ip *)pkt)->ip_len) << 3);
575 			break;
576 
577 		default:
578 			printf("imp%d: can't handle af%d\n", ifp->if_unit,
579 				dst->sa_family);
580 			error = EAFNOSUPPORT;
581 			m0 = m;
582 			goto drop;
583 		}
584 	}
585 	return (impsnd(ifp, m));
586 drop:
587 	m_freem(m0);
588 	return (error);
589 }
590 
591 /*
592  * Put a message on an interface's output queue.
593  * Perform RFNM counting: no more than 8 message may be
594  * in flight to any one host.
595  */
596 impsnd(ifp, m)
597 	struct ifnet *ifp;
598 	struct mbuf *m;
599 {
600 	register struct control_leader *imp;
601 	register struct host *hp;
602 	register struct imp_softc *sc = &imp_softc[ifp->if_unit];
603 	int s, error = 0;
604 
605 	imp = mtod(m, struct control_leader *);
606 
607 	/*
608 	 * Do RFNM counting for data messages
609 	 * (no more than 8 outstanding to any host).
610 	 * Queue data messages per host if 8 are already outstanding
611 	 * or if the hardware interface is already doing output.
612 	 * Increment imp_msgready if the message could be sent now,
613 	 * but must be queued because the imp output is busy.
614 	 */
615 	s = splimp();
616 	if (imp->dl_mtype == IMPTYPE_DATA) {
617 		hp = hostenter((int)imp->dl_imp, (int)imp->dl_host,
618 		    ifp->if_unit);
619 		if (hp) {
620 			if (hp->h_flags & (HF_DEAD|HF_UNREACH))
621 				error = hp->h_flags & HF_DEAD ?
622 				    EHOSTDOWN : EHOSTUNREACH;
623 			else if (hp->h_rfnm < IMP_MAXHOSTMSG &&
624 			    sc->imp_cb.ic_oactive == 0) {
625 				/*
626 				 * Send without queuing;
627 				 * adjust rfnm count and timer.
628 				 */
629 				if (hp->h_rfnm++ == 0)
630 				    hp->h_timer = RFNMTIMER;
631 				goto send;
632 			} else if (hp->h_rfnm + hp->h_qcnt < imphqlen) {
633 				HOST_ENQUE(hp, m);
634 				if (hp->h_rfnm + hp->h_qcnt <= IMP_MAXHOSTMSG)
635 					sc->imp_msgready++;
636 			} else {
637 				error = ENOBUFS;
638 				IF_DROP(&ifp->if_snd);
639 			}
640 		} else
641 			error = ENOBUFS;
642 	} else if (sc->imp_cb.ic_oactive == 0)
643 		goto send;
644 	else
645 		IF_ENQUEUE(&ifp->if_snd, m);
646 
647 	splx(s);
648 	if (error)
649 		m_freem(m);
650 	return (error);
651 
652 send:
653 	sc->imp_if.if_timer = IMP_OTIMER;
654 	(*sc->imp_cb.ic_output)(sc->imp_cb.ic_hwunit, m);
655 	splx(s);
656 	return (0);
657 }
658 
659 /*
660  * Start another output operation on IMP; called from hardware
661  * transmit-complete interrupt routine at splimp or from imp routines
662  * when output is not in progress.  If there are any packets on shared
663  * output queue, send them, otherwise send the next data packet for a host.
664  * Host data packets are sent round-robin based on destination by walking
665  * the host list.
666  */
667 impstart(sc)
668 	register struct imp_softc *sc;
669 {
670 	register struct mbuf *m;
671 	int first = 1;				/* XXX */
672 	register struct host *hp;
673 	int index;
674 
675 	IF_DEQUEUE(&sc->imp_if.if_snd, m);
676 	if (m) {
677 		sc->imp_if.if_timer = IMP_OTIMER;
678 		(*sc->imp_cb.ic_output)(sc->imp_cb.ic_hwunit, m);
679 		return;
680 	}
681 	if (sc->imp_msgready) {
682 		if ((m = sc->imp_hostq) == 0 && (m = sc->imp_hosts) == 0)
683 			panic("imp msgready");
684 		index = sc->imp_hostent;
685 		for (hp = &mtod(m, struct hmbuf *)->hm_hosts[index]; ;
686 		    hp++, index++) {
687 			if (index >= HPMBUF) {
688 				if ((m = m->m_next) == 0)
689 					m = sc->imp_hosts;
690 				index = 0;
691 				hp = mtod(m, struct hmbuf *)->hm_hosts;
692 				first = 0;		/* XXX */
693 			}
694 			if (hp->h_qcnt && hp->h_rfnm < IMP_MAXHOSTMSG) {
695 				/*
696 				 * Found host entry with another message
697 				 * to send.  Deliver it to the IMP.
698 				 * Start with succeeding host next time.
699 				 */
700 				impstarthost(sc, hp);
701 				sc->imp_hostq = m;
702 				sc->imp_hostent = index + 1;
703 				return;
704 			}
705 			if (m == sc->imp_hostq && !first &&
706 			    index + 1 >= sc->imp_hostent) {	/* XXX */
707 				log(imppri, "imp: can't find %d msgready\n",
708 				    sc->imp_msgready);
709 				sc->imp_msgready = 0;
710 				break;
711 			}
712 		}
713 	}
714 	sc->imp_if.if_timer = 0;
715 }
716 
717 /*
718  * Restart output for a host that has received a RFNM
719  * or incomplete or has timed out while waiting for a RFNM.
720  * Must be called at splimp.
721  */
722 imprestarthost(sc, hp)
723 	register struct imp_softc *sc;
724 	struct host *hp;
725 {
726 
727 	if (--hp->h_rfnm > 0)
728 		hp->h_timer = RFNMTIMER;
729 	/*
730 	 * If the RFNM moved a queued message into the window,
731 	 * update msgready and start IMP if idle.
732 	 */
733 	if (hp->h_qcnt > IMP_MAXHOSTMSG - 1 - hp->h_rfnm) {
734 		sc->imp_msgready++;
735 		if (sc->imp_cb.ic_oactive == 0)
736 			impstarthost(sc, hp);
737 	}
738 	if (hp->h_rfnm == 0 && hp->h_qcnt == 0)
739 		hostidle(hp);
740 }
741 
742 /*
743  * Send the next message queued for a host
744  * when ready to send another message to the IMP.
745  * Called only when output is not in progress.
746  * Bump RFNM counter and start RFNM timer
747  * when we send the message to the IMP.
748  * Must be called at splimp.
749  */
750 impstarthost(sc, hp)
751 	register struct imp_softc *sc;
752 	register struct host *hp;
753 {
754 	struct mbuf *m;
755 
756 	if (hp->h_rfnm++ == 0)
757 		hp->h_timer = RFNMTIMER;
758 	HOST_DEQUE(hp, m);
759 	sc->imp_if.if_timer = IMP_OTIMER;
760 	(*sc->imp_cb.ic_output)(sc->imp_cb.ic_hwunit, m);
761 	sc->imp_msgready--;
762 }
763 
764 /*
765  * "Watchdog" timeout.  When the output timer expires,
766  * we assume we have been blocked by the imp.
767  * No need to restart, just collect statistics.
768  */
769 imptimo(unit)
770 	int unit;
771 {
772 
773 	imp_softc[unit].imp_block++;
774 }
775 
776 /*
777  * Put three 1822 NOOPs at the head of the output queue.
778  * Part of host-IMP initialization procedure.
779  * (Should return success/failure, but noone knows
780  * what to do with this, so why bother?)
781  * This routine is always called at splimp, so we don't
782  * protect the call to IF_PREPEND.
783  */
784 impnoops(sc)
785 	register struct imp_softc *sc;
786 {
787 	register i;
788 	register struct mbuf *m;
789 	register struct control_leader *cp;
790 
791 #ifdef IMPINIT
792 	if (imptraceinit)
793 		log(imppri, "impnoops\n");
794 #endif
795 	for (i = 0; i < IMP_NOOPCNT; i++) {
796 		if ((m = m_getclr(M_DONTWAIT, MT_HEADER)) == 0)
797 			return;
798 		m->m_len = sizeof(struct control_leader);
799 		cp = mtod(m, struct control_leader *);
800 		cp->dl_format = IMP_NFF;
801 		cp->dl_link = i;
802 		cp->dl_mtype = IMPTYPE_NOOP;
803 		IF_PREPEND(&sc->imp_if.if_snd, m);
804 	}
805 	if (sc->imp_cb.ic_oactive == 0)
806 		impstart(sc);
807 }
808 
809 /*
810  * Process an ioctl request.
811  */
812 impioctl(ifp, cmd, data)
813 	register struct ifnet *ifp;
814 	int cmd;
815 	caddr_t data;
816 {
817 	struct ifaddr *ifa = (struct ifaddr *) data;
818 	int s = splimp(), error = 0;
819 #define sc	((struct imp_softc *)ifp)
820 
821 	switch (cmd) {
822 
823 	case SIOCSIFADDR:
824 		if (ifa->ifa_addr.sa_family != AF_INET) {
825 			error = EINVAL;
826 			break;
827 		}
828 		if ((ifp->if_flags & IFF_UP) == 0)
829 			impinit(ifp->if_unit);
830 		break;
831 
832 	case SIOCSIFFLAGS:
833 		if ((ifp->if_flags & IFF_UP) == 0 &&
834 		    sc->imp_state != IMPS_DOWN) {
835 			if (sc->imp_cb.ic_down &&
836 			    (*sc->imp_cb.ic_down)(sc->imp_cb.ic_hwunit)) {
837 				sc->imp_state = IMPS_DOWN;
838 				sc->imp_msgready = 0;
839 				hostreset(ifp->if_unit);
840 				if_down(ifp);
841 			}
842 		} else if (ifp->if_flags & IFF_UP && sc->imp_state == IMPS_DOWN)
843 			impinit(ifp->if_unit);
844 		break;
845 
846 	default:
847 		error = EINVAL;
848 		break;
849 	}
850 	splx(s);
851 	return (error);
852 }
853 
854 #ifdef IMPLEADERS
855 printleader(routine, ip)
856 	char *routine;
857 	register struct imp_leader *ip;
858 {
859 	printf("%s: ", routine);
860 	printbyte((char *)ip, 12);
861 	printf("<fmt=%x,net=%x,flags=%x,mtype=", ip->il_format, ip->il_network,
862 		ip->il_flags);
863 	if (ip->il_mtype <= IMPTYPE_READY)
864 		printf("%s,", impleaders[ip->il_mtype]);
865 	else
866 		printf("%x,", ip->il_mtype);
867 	printf("htype=%x,host=%x,imp=%x,link=", ip->il_htype, ip->il_host,
868 		ntohs(ip->il_imp));
869 	if (ip->il_link == IMPLINK_IP)
870 		printf("ip,");
871 	else
872 		printf("%x,", ip->il_link);
873 	printf("subtype=%x,len=%x>\n",ip->il_subtype,ntohs(ip->il_length)>>3);
874 }
875 
876 printbyte(cp, n)
877 	register char *cp;
878 	int n;
879 {
880 	register i, j, c;
881 
882 	for (i=0; i<n; i++) {
883 		c = *cp++;
884 		for (j=0; j<2; j++)
885 			putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf], 0);
886 		putchar(' ', 0);
887 	}
888 	putchar('\n', 0);
889 }
890 #endif
891 
892 /*
893  * Routine to convert from IMP Leader to InterNet Address.
894  *
895  * This procedure is necessary because IMPs may be assigned Class A, B, or C
896  * network numbers, but only have 8 bits in the leader to reflect the
897  * IMP "network number".  The strategy is to take the network number from
898  * the ifnet structure, and blend in the host-on-imp and imp-on-net numbers
899  * from the leader.
900  *
901  * There is no support for "Logical Hosts".
902  *
903  * Class A:	Net.Host.0.Imp
904  * Class B:	Net.net.Host.Imp
905  * Class C:	Net.net.net.(Host4|Imp4)
906  */
907 imp_leader_to_addr(ap, cp, ifp)
908 	struct in_addr *ap;
909 	register struct control_leader *cp;
910 	struct ifnet *ifp;
911 {
912 	register u_long final;
913 	register struct sockaddr_in *sin;
914 	int imp = ntohs(cp->dl_imp);
915 
916 	sin = (struct sockaddr_in *)(&ifp->if_addrlist->ifa_addr);
917 	final = ntohl(sin->sin_addr.s_addr);
918 
919 	if (IN_CLASSA(final)) {
920 		final &= IN_CLASSA_NET;
921 		final |= (imp & 0xFF) | ((cp->dl_host & 0xFF)<<16);
922 	} else if (IN_CLASSB(final)) {
923 		final &= IN_CLASSB_NET;
924 		final |= (imp & 0xFF) | ((cp->dl_host & 0xFF)<<8);
925 	} else {
926 		final &= IN_CLASSC_NET;
927 		final |= (imp & 0x0F) | ((cp->dl_host & 0x0F)<<4);
928 	}
929 	ap->s_addr = htonl(final);
930 }
931 
932 /*
933  * Function to take InterNet address and fill in IMP leader fields.
934  */
935 imp_addr_to_leader(imp, a)
936 	register struct control_leader *imp;
937 	u_long a;
938 {
939 	register u_long addr = ntohl(a);
940 
941 	imp->dl_network = 0;	/* !! */
942 
943 	if (IN_CLASSA(addr)) {
944 		imp->dl_host = ((addr>>16) & 0xFF);
945 		imp->dl_imp = addr & 0xFF;
946 	} else if (IN_CLASSB(addr)) {
947 		imp->dl_host = ((addr>>8) & 0xFF);
948 		imp->dl_imp = addr & 0xFF;
949 	} else {
950 		imp->dl_host = ((addr>>4) & 0xF);
951 		imp->dl_imp = addr & 0xF;
952 	}
953 	imp->dl_imp = htons(imp->dl_imp);
954 }
955 #endif
956