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