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