first "working" imp driver
[unix-history] / usr / src / sys / deprecated / netimp / if_imp.c
CommitLineData
41e530c7 1/* if_imp.c 4.5 82/02/16 */
6c73ade6
BJ
2
3#include "imp.h"
4#if NIMP > 0
5/*
6 * ARPAnet IMP interface driver.
7 *
8 * The IMP-host protocol is handled here, leaving
9 * hardware specifics to the lower level interface driver.
a2cd4df7
BJ
10 *
11 * TODO:
12 * rethink coupling between this module and device driver
13 * pass more error indications up to protocol modules
6c73ade6
BJ
14 */
15#include "../h/param.h"
16#include "../h/systm.h"
17#include "../h/mbuf.h"
18#include "../h/pte.h"
19#include "../h/buf.h"
20#include "../h/protosw.h"
21#include "../h/socket.h"
22#include "../h/ubareg.h"
23#include "../h/ubavar.h"
24#include "../h/cpu.h"
25#include "../h/mtpr.h"
26#include "../h/vmmac.h"
27#include "../net/in.h"
28#include "../net/in_systm.h"
29#include "../net/if.h"
30#include "../net/if_imp.h"
41e530c7 31#include "../net/if_imphost.h"
6c73ade6
BJ
32#include "../net/ip.h"
33#include "../net/ip_var.h"
34
35/*
36 * IMP software status per interface.
37 * (partially shared with the hardware specific module)
38 *
39 * Each interface is referenced by a network interface structure,
40 * imp_if, which the routing code uses to locate the interface.
41 * This structure contains the output queue for the interface, its
42 * address, ... IMP specific structures used in connecting the
43 * IMP software modules to the hardware specific interface routines
a2cd4df7
BJ
44 * are stored here. The common structures are made visible to the
45 * interface driver by passing a pointer to the hardware routine
46 * at "attach" time.
6c73ade6
BJ
47 *
48 * NOTE: imp_if and imp_cb are assumed adjacent in hardware code.
49 */
50struct imp_softc {
51 struct ifnet imp_if; /* network visible interface */
52 struct impcb imp_cb; /* hooks to hardware module */
53 u_char imp_state; /* current state of IMP */
54 char imp_dropcnt; /* used during initialization */
6c73ade6
BJ
55} imp_softc[NIMP];
56
57/*
58 * Messages from IMP regarding why
59 * it's going down.
60 */
61static char *impmsg[] = {
62 "in 30 seconds",
63 "for hardware PM",
64 "to reload software",
65 "for emergency reset"
66};
67
a2cd4df7
BJ
68int impdown(), impinit(), impoutput();
69
6c73ade6
BJ
70/*
71 * IMP attach routine. Called from hardware device attach routine
72 * at configuration time with a pointer to the UNIBUS 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 */
77impattach(ui)
78 struct uba_device *ui;
79{
80 struct imp_softc *sc = &imp_softc[ui->ui_unit];
81 register struct ifnet *ifp = &sc->imp_if;
82
83COUNT(IMPATTACH);
84 /* UNIT COULD BE AMBIGUOUS */
85 ifp->if_unit = ui->ui_unit;
86 ifp->if_name = "imp";
87 ifp->if_mtu = IMP_MTU;
88 ifp->if_net = ui->ui_flags;
a2cd4df7
BJ
89#ifdef notdef
90 /* this should get cleaned after we talk to the imp */
91 ifp->if_addr = if_makeaddr(ifp->if_net, ifp->if_host);
92#endif
93 ifp->if_init = impinit;
94 ifp->if_output = impoutput;
95 /* reset is handled at the hardware level */
6c73ade6
BJ
96 if_attach(ifp);
97 /* kludge to hand pointers back to hardware attach routine */
98 return ((int)&sc->imp_if);
99}
100
101/*
102 * IMP initialization routine: call hardware module to
103 * setup UNIBUS resources, init state and get ready for
104 * NOOPs the IMP should send us, and that we want to drop.
105 */
106impinit(unit)
107 int unit;
108{
109 register struct imp_softc *sc = &imp_softc[unit];
110
a2cd4df7
BJ
111 if ((*sc->imp_cb.ic_init)(unit) == 0) {
112 sc->imp_state = IMPS_DOWN;
113 return;
114 }
6c73ade6
BJ
115 sc->imp_state = IMPS_INIT;
116 sc->imp_dropcnt = IMP_DROPCNT;
a2cd4df7 117 impnoops(sc);
6c73ade6
BJ
118}
119
120struct sockproto impproto = { PF_IMPLINK };
faad37c0
SL
121struct sockaddr_in impdst = { AF_IMPLINK };
122struct sockaddr_in impsrc = { AF_IMPLINK };
6c73ade6
BJ
123
124/*
125 * ARPAnet 1822 input routine.
126 * Called from hardware input interrupt routine to handle 1822
127 * IMP-host messages. Type 0 messages (non-control) are
128 * passed to higher level protocol processors on the basis
129 * of link number. Other type messages (control) are handled here.
130 */
a2cd4df7 131impinput(unit, m)
6c73ade6 132 int unit;
a2cd4df7 133 register struct mbuf *m;
6c73ade6
BJ
134{
135 int s;
6c73ade6
BJ
136 register struct imp_leader *ip;
137 register struct imp_softc *sc = &imp_softc[unit];
138 register struct host *hp;
139 register struct ifqueue *inq;
a2cd4df7 140 struct control_leader *cp;
6c73ade6
BJ
141 struct in_addr addr;
142
143COUNT(IMP_INPUT);
a0b7c7fb 144 /*
a2cd4df7
BJ
145 * Verify leader length. Be careful with control
146 * message which don't get a length included.
a0b7c7fb
SL
147 * We should generate a "bad leader" message
148 * to the IMP about messages too short.
149 */
a2cd4df7
BJ
150 if (m->m_len < sizeof(struct control_leader) &&
151 (m = m_pullup(m, sizeof(struct control_leader))) == 0)
152 return;
153 cp = mtod(m, struct control_leader *);
154 if (cp->dl_mtype == IMPTYPE_DATA)
155 if (m->m_len < sizeof(struct imp_leader) &&
156 (m = m_pullup(m, sizeof(struct imp_leader))) == 0)
157 return;
6c73ade6
BJ
158 ip = mtod(m, struct imp_leader *);
159
a0b7c7fb
SL
160 /*
161 * Check leader type -- should notify IMP
162 * in case of failure...
163 */
a2cd4df7
BJ
164 if (ip->il_format != IMP_NFF) {
165 sc->imp_if.if_collisions++; /* XXX */
6c73ade6 166 goto drop;
a2cd4df7 167 }
6c73ade6
BJ
168
169 /*
170 * Certain messages require a host structure.
171 * Do this in one shot here.
172 */
173 switch (ip->il_mtype) {
174
175 case IMPTYPE_RFNM:
176 case IMPTYPE_INCOMPLETE:
177 case IMPTYPE_HOSTDEAD:
178 case IMPTYPE_HOSTUNREACH:
179 case IMPTYPE_BADDATA:
41e530c7 180#ifdef notdef
a2cd4df7 181 addr.s_net = ip->il_network;
41e530c7
BJ
182#else
183 addr.s_net = 0;
184#endif
a2cd4df7
BJ
185 addr.s_imp = ip->il_imp;
186 addr.s_host = ip->il_host;
187 hp = hostlookup(addr);
6c73ade6
BJ
188 break;
189 }
190
191 switch (ip->il_mtype) {
192
193 /*
194 * Data for a protocol. Dispatch to the appropriate
195 * protocol routine (running at software interrupt).
196 * If this isn't a raw interface, advance pointer
a0b7c7fb 197 * into mbuf past leader (done below).
6c73ade6
BJ
198 */
199 case IMPTYPE_DATA:
a2cd4df7
BJ
200 ip->il_length =
201 (ntohs(ip->il_length) >> 3) - sizeof(struct imp_leader);
6c73ade6
BJ
202 break;
203
204 /*
205 * IMP leader error. Reset the IMP and discard the packet.
206 */
207 case IMPTYPE_BADLEADER:
a0b7c7fb
SL
208 /*
209 * According to 1822 document, this message
210 * will be generated in response to the
211 * first noop sent to the IMP after
212 * the host resets the IMP interface.
213 */
a2cd4df7 214 if (sc->imp_state != IMPS_INIT) {
a0b7c7fb 215 imperr(sc, "leader error");
a2cd4df7 216 hostreset(sc->imp_if.if_net); /* XXX */
a0b7c7fb
SL
217 impnoops(sc);
218 }
6c73ade6
BJ
219 goto drop;
220
221 /*
222 * IMP going down. Print message, and if not immediate,
223 * set off a timer to insure things will be reset at the
224 * appropriate time.
225 */
226 case IMPTYPE_DOWN:
227 if ((ip->il_link & IMP_DMASK) == 0) {
228 sc->imp_state = IMPS_GOINGDOWN;
a2cd4df7 229 timeout(impdown, sc, 30 * hz);
6c73ade6
BJ
230 }
231 imperr(sc, "going down %s", impmsg[ip->il_link & IMP_DMASK]);
232 goto drop;
233
234 /*
235 * A NOP usually seen during the initialization sequence.
236 * Compare the local address with that in the message.
237 * Reset the local address notion if it doesn't match.
238 */
a2cd4df7
BJ
239 case IMPTYPE_NOOP: {
240 register struct in_addr *sin;
241
a0b7c7fb
SL
242 if (sc->imp_state == IMPS_DOWN) {
243 sc->imp_state = IMPS_INIT;
244 sc->imp_dropcnt = IMP_DROPCNT;
245 }
a2cd4df7
BJ
246 if (sc->imp_state != IMPS_INIT)
247 goto drop;
248 if (--sc->imp_dropcnt > 0)
249 goto drop;
250 sc->imp_state = IMPS_UP;
251 sin = &sc->imp_if.if_addr;
252 sc->imp_if.if_host[0] = sin->s_host = ip->il_host;
253 sin->s_imp = ip->il_imp;
254 imperr(sc, "reset (host %d/imp %d)", ip->il_host,
255 ntohs(ip->il_imp));
256 /* restart output in case something was q'd */
257 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit);
6c73ade6 258 goto drop;
a2cd4df7 259 }
6c73ade6
BJ
260
261 /*
262 * RFNM or INCOMPLETE message, record in
263 * host table and prime output routine.
264 *
a0b7c7fb 265 * SHOULD NOTIFY PROTOCOL ABOUT INCOMPLETES.
6c73ade6
BJ
266 */
267 case IMPTYPE_RFNM:
268 case IMPTYPE_INCOMPLETE:
269 if (hp && hp->h_rfnm) {
270 register struct mbuf *n;
271
272 hp->h_rfnm--;
273 /* poke holding queue */
274 if (n = hp->h_q) {
a2cd4df7 275 if (n->m_next == n)
6c73ade6
BJ
276 hp->h_q = 0;
277 else {
a2cd4df7
BJ
278 n = n->m_next;
279 hp->h_q->m_next = n->m_next;
6c73ade6 280 }
a2cd4df7
BJ
281 (void) impsnd(sc, n);
282 break;
6c73ade6 283 }
a2cd4df7
BJ
284 if (hp->h_rfnm == 0)
285 hostfree(hp);
6c73ade6 286 }
41e530c7 287 goto rawlinkin;
6c73ade6
BJ
288
289 /*
290 * Host or IMP can't be reached. Flush any packets
291 * awaiting transmission and release the host structure.
292 *
a2cd4df7 293 * TODO: NOTIFY THE PROTOCOL
6c73ade6
BJ
294 */
295 case IMPTYPE_HOSTDEAD:
a2cd4df7
BJ
296 imperr(sc, "host dead"); /* XXX */
297 goto common; /* XXX */
298
299 /* SHOULD SIGNAL ROUTING DAEMON */
6c73ade6 300 case IMPTYPE_HOSTUNREACH:
a2cd4df7
BJ
301 imperr(sc, "host unreachable"); /* XXX */
302 common:
6c73ade6 303 if (hp)
a2cd4df7 304 hostfree(hp); /* won't work right */
41e530c7 305 goto rawlinkin;
6c73ade6
BJ
306
307 /*
308 * Error in data. Clear RFNM status for this host and send
309 * noops to the IMP to clear the interface.
310 */
311 case IMPTYPE_BADDATA:
312 imperr(sc, "data error");
313 if (hp)
314 hp->h_rfnm = 0;
315 impnoops(sc);
41e530c7 316 goto rawlinkin;
6c73ade6
BJ
317
318 /*
a0b7c7fb 319 * Interface reset.
6c73ade6
BJ
320 */
321 case IMPTYPE_RESET:
a0b7c7fb 322 imperr(sc, "interface reset");
a0b7c7fb 323 impnoops(sc);
6c73ade6
BJ
324 goto drop;
325
326 default:
327 sc->imp_if.if_collisions++; /* XXX */
328 goto drop;
329 }
330
331 /*
332 * Queue on protocol's input queue.
333 */
334 switch (ip->il_link) {
335
336#ifdef INET
337 case IMPLINK_IP:
338 m->m_len -= sizeof(struct imp_leader);
339 m->m_off += sizeof(struct imp_leader);
340 setipintr();
341 inq = &ipintrq;
342 break;
343#endif
344
345 default:
41e530c7 346rawlinkin:
6c73ade6 347 impproto.sp_protocol = ip->il_link;
faad37c0
SL
348 impdst.sin_addr = sc->imp_if.if_addr;
349 impsrc.sin_addr.s_net = ip->il_network;
350 impsrc.sin_addr.s_host = ip->il_host;
351 impsrc.sin_addr.s_imp = ip->il_imp;
a2cd4df7 352 raw_input(m, &impproto, &impdst, &impsrc);
6c73ade6
BJ
353 return;
354 }
355 IF_ENQUEUE(inq, m);
356 return;
357
358drop:
359 m_freem(m);
360}
361
a0b7c7fb
SL
362/*
363 * Bring the IMP down after notification.
364 */
365impdown(sc)
366 struct imp_softc *sc;
367{
368 sc->imp_state = IMPS_DOWN;
a2cd4df7 369 imperr(sc, "marked down");
a0b7c7fb
SL
370 /* notify protocols with messages waiting? */
371}
372
6c73ade6
BJ
373/*VARARGS*/
374imperr(sc, fmt, a1, a2)
375 struct imp_softc *sc;
376 char *fmt;
377{
378 printf("imp%d: ", sc->imp_if.if_unit);
379 printf(fmt, a1, a2);
380 printf("\n");
381}
382
383/*
384 * ARPAnet 1822 output routine.
385 * Called from higher level protocol routines to set up messages for
386 * transmission to the imp. Sets up the header and calls impsnd to
387 * enqueue the message for this IMP's hardware driver.
388 */
389impoutput(ifp, m0, pf)
390 register struct ifnet *ifp;
391 struct mbuf *m0;
392{
393 register struct imp_leader *imp;
394 register struct mbuf *m = m0;
a2cd4df7 395 int x, dhost, dimp, dlink, len, dnet;
6c73ade6 396
a2cd4df7 397COUNT(IMPOUTPUT);
a2cd4df7 398#ifdef notdef
6c73ade6
BJ
399 /*
400 * Don't even try if the IMP is unavailable.
401 */
a0b7c7fb
SL
402 x = imp_softc[ifp->if_unit].imp_state;
403 if (x == IMPS_DOWN || x == IMPS_GOINGDOWN)
404 goto drop;
a2cd4df7 405#endif
6c73ade6
BJ
406
407 switch (pf) {
408
409#ifdef INET
410 case PF_INET: {
411 register struct ip *ip = mtod(m0, struct ip *);
412
a2cd4df7 413 dnet = ip->ip_dst.s_net;
6c73ade6
BJ
414 dhost = ip->ip_dst.s_host;
415 dimp = ip->ip_dst.s_imp;
416 dlink = IMPLINK_IP;
417 len = ntohs(ip->ip_len);
418 break;
419 }
420#endif
421 case PF_IMPLINK:
422 goto leaderexists;
423
424 default:
425 printf("imp%d: can't encapsulate pf%d\n", ifp->if_unit, pf);
a0b7c7fb 426 goto drop;
6c73ade6
BJ
427 }
428
429 /*
430 * Add IMP leader. If there's not enough space in the
431 * first mbuf, allocate another. If that should fail, we
432 * drop this sucker.
433 */
434 if (m->m_off > MMAXOFF ||
435 MMINOFF + sizeof(struct imp_leader) > m->m_off) {
436 m = m_get(M_DONTWAIT);
a0b7c7fb
SL
437 if (m == 0)
438 goto drop;
6c73ade6
BJ
439 m->m_next = m0;
440 m->m_off = MMINOFF;
441 m->m_len = sizeof(struct imp_leader);
442 } else {
443 m->m_off -= sizeof(struct imp_leader);
444 m->m_len += sizeof(struct imp_leader);
445 }
446 imp = mtod(m, struct imp_leader *);
447 imp->il_format = IMP_NFF;
41e530c7 448 imp->il_mtype = IMPTYPE_DATA;
a2cd4df7 449 imp->il_network = dnet;
6c73ade6 450 imp->il_host = dhost;
a2cd4df7
BJ
451 imp->il_imp = dimp;
452 imp->il_length = htons((len + sizeof(struct imp_leader)) << 3);
6c73ade6 453 imp->il_link = dlink;
41e530c7 454 imp->il_flags = imp->il_htype = imp->il_subtype = 0;
6c73ade6
BJ
455
456leaderexists:
457 /*
458 * Hand message to impsnd to perform RFNM counting
459 * and eventual transmission.
460 */
461 return (impsnd(ifp, m));
a0b7c7fb
SL
462drop:
463 m_freem(m0);
464 return (0);
6c73ade6
BJ
465}
466
467/*
468 * Put a message on an interface's output queue.
469 * Perform RFNM counting: no more than 8 message may be
470 * in flight to any one host.
471 */
472impsnd(ifp, m)
473 struct ifnet *ifp;
474 struct mbuf *m;
475{
476 register struct imp_leader *ip;
477 register struct host *hp;
478 struct impcb *icp;
479 int x;
480
a2cd4df7 481COUNT(IMPSND);
6c73ade6
BJ
482 ip = mtod(m, struct imp_leader *);
483
484 /*
485 * Do RFNM counting for data messages
486 * (no more than 8 outstanding to any host)
487 */
488 if (ip->il_mtype == IMPTYPE_DATA) {
489 struct in_addr addr;
490
41e530c7 491#ifdef notdef
a2cd4df7 492 addr.s_net = ip->il_network;
41e530c7
BJ
493#else
494 addr.s_net = 0;
495#endif
6c73ade6
BJ
496 addr.s_host = ip->il_host;
497 addr.s_imp = ip->il_imp;
41e530c7 498 x = splimp();
a2cd4df7
BJ
499 if ((hp = hostlookup(addr)) == 0)
500 hp = hostenter(addr);
6c73ade6
BJ
501
502 /*
a0b7c7fb 503 * If IMP would block, queue until RFNM
6c73ade6
BJ
504 */
505 if (hp) {
506 register struct mbuf *n;
507 int cnt;
508
509 if (hp->h_rfnm < 8) {
510 hp->h_rfnm++;
41e530c7 511 splx(x);
6c73ade6
BJ
512 goto enque;
513 }
514 /*
515 * Keeping the count in the host structure
516 * causes the packing scheme to lose too much.
517 */
a2cd4df7
BJ
518 cnt = 0;
519 if (n = hp->h_q)
520 for (; n != hp->h_q; n = n->m_next)
521 cnt++;
6c73ade6
BJ
522 if (cnt >= 8)
523 goto drop;
a0b7c7fb
SL
524
525 /*
a2cd4df7 526 * Q is kept as circular list with h_q
a0b7c7fb
SL
527 * (head) pointing to the last entry.
528 */
6c73ade6 529 if ((n = hp->h_q) == 0)
a2cd4df7 530 hp->h_q = m->m_next = m;
6c73ade6 531 else {
a2cd4df7
BJ
532 m->m_next = n->m_next;
533 hp->h_q = n->m_next = m;
6c73ade6 534 }
41e530c7 535 splx(x);
6c73ade6
BJ
536 goto start;
537 }
538drop:
539 m_freem(m);
41e530c7 540 splx(x);
6c73ade6
BJ
541 return (0);
542 }
543enque:
544 x = splimp();
545 IF_ENQUEUE(&ifp->if_snd, m);
546 splx(x);
547
548start:
549 icp = &imp_softc[ifp->if_unit].imp_cb;
550 if (icp->ic_oactive == 0)
551 (*icp->ic_start)(ifp->if_unit);
552 return (1);
553}
554
555/*
556 * Put three 1822 NOOPs at the head of the output queue.
557 * Part of host-IMP initialization procedure.
558 * (Should return success/failure, but noone knows
559 * what to do with this, so why bother?)
560 */
561impnoops(sc)
562 register struct imp_softc *sc;
563{
564 register i;
565 register struct mbuf *m;
a2cd4df7 566 register struct control_leader *cp;
6c73ade6
BJ
567 int x;
568
a2cd4df7 569COUNT(IMPNOOPS);
6c73ade6
BJ
570 sc->imp_state = IMPS_INIT;
571 sc->imp_dropcnt = IMP_DROPCNT;
a2cd4df7 572 for (i = 0; i < IMP_DROPCNT + 1; i++ ) {
6c73ade6
BJ
573 if ((m = m_getclr(M_DONTWAIT)) == 0)
574 return;
575 m->m_off = MMINOFF;
a2cd4df7
BJ
576 m->m_len = sizeof(struct control_leader);
577 cp = mtod(m, struct control_leader *);
578 cp->dl_format = IMP_NFF;
579 cp->dl_link = i;
580 cp->dl_mtype = IMPTYPE_NOOP;
581#ifdef notdef
582 cp->dl_network = sc->imp_if.if_net; /* XXX */
583 cp->dl_host = sc->imp_if.if_addr.s_host;/* XXX */
584 cp->dl_imp = sc->imp_if.if_addr.s_imp; /* XXX */
585#endif
6c73ade6
BJ
586 x = splimp();
587 IF_PREPEND(&sc->imp_if.if_snd, m);
588 splx(x);
589 }
590 if (sc->imp_cb.ic_oactive == 0)
591 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit);
592}
a2cd4df7 593
41e530c7 594#ifdef IMPLEADERS
a2cd4df7
BJ
595printleader(routine, ip)
596 char *routine;
597 register struct imp_leader *ip;
598{
599 printf("%s: ", routine);
600 printbyte((char *)ip, 12);
601 printf("<fmt=%x,net=%x,flags=%x,mtype=", ip->il_format, ip->il_network,
602 ip->il_flags);
603 if (ip->il_mtype <= IMPTYPE_READY)
604 printf("%s,", impleaders[ip->il_mtype]);
605 else
606 printf("%x,", ip->il_mtype);
607 printf("htype=%x,host=%x,imp=%x,link=", ip->il_htype, ip->il_host,
608 ip->il_impno);
609 if (ip->il_link == IMPLINK_IP)
610 printf("ip,");
611 else
612 printf("%x,", ip->il_link);
613 printf("subtype=%x,len=%x>\n",ip->il_subtype,ntohs(ip->il_length)>>3);
614}
615
616printbyte(cp, n)
617 register char *cp;
618 int n;
619{
620 register i, j, c;
621
622 for (i=0; i<n; i++) {
623 c = *cp++;
624 for (j=0; j<2; j++)
625 putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf]);
626 putchar(' ');
627 }
628 putchar('\n');
629}
6c73ade6 630#endif
41e530c7 631#endif