one more time
[unix-history] / usr / src / sys / vax / if / if_il.c
CommitLineData
34a8182d 1/* if_il.c 4.4 82/05/27 */
f0d51478
BF
2
3#include "il.h"
4#include "imp.h"
5#include "loop.h"
6
7/*
8 * Interlan Ethernet Communications Controller interface
9 */
10
11#include "../h/param.h"
12#include "../h/systm.h"
13#include "../h/mbuf.h"
14#include "../h/pte.h"
15#include "../h/buf.h"
16#include "../h/protosw.h"
17#include "../h/socket.h"
18#include "../h/ubareg.h"
19#include "../h/ubavar.h"
20#include "../h/ilreg.h"
21#include "../h/cpu.h"
22#include "../h/mtpr.h"
23#include "../h/vmmac.h"
24#include "../net/in.h"
25#include "../net/in_systm.h"
26#include "../net/if.h"
27#include "../net/if_il.h"
28#include "../net/if_uba.h"
29#include "../net/ip.h"
30#include "../net/ip_var.h"
31#include "../net/pup.h"
32#include "../net/route.h"
33#include <errno.h>
34
35#define ILMTU 1500
36
37int ilprobe(), ilattach(), ilrint(), ilcint();
38struct uba_device *ilinfo[NIL];
39u_short ilstd[] = { 0 };
40struct uba_driver ildriver =
41 { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo };
42u_char il_ectop[3] = { 0x02, 0x60, 0x8c };
43#define ILUNIT(x) minor(x)
44
45int ilinit(),iloutput(),ilreset();
46
47/*
48 * Ethernet software status per interface.
49 *
50 * Each interface is referenced by a network interface structure,
51 * is_if, which the routing code uses to locate the interface.
52 * This structure contains the output queue for the interface, its address, ...
53 * We also have, for each interface, a UBA interface structure, which
54 * contains information about the UNIBUS resources held by the interface:
55 * map registers, buffered data paths, etc. Information is cached in this
56 * structure for use by the if_uba.c routines in running the interface
57 * efficiently.
58 */
59struct il_softc {
60 struct ifnet is_if; /* network-visible interface */
61 struct ifuba is_ifuba; /* UNIBUS resources */
62 short is_oactive; /* is output active? */
63 short is_startrcv; /* hang receive next chance */
64 u_char is_enaddr[6]; /* board's ethernet address */
65} il_softc[NIL];
66
67/*
68 * Do an OFFLINE command. This will cause an interrupt for the
69 * autoconfigure stuff.
70 */
71ilprobe(reg)
72 caddr_t reg;
73{
74 register int br, cvec; /* r11, r10 value-result */
75 register struct ildevice *addr = (struct ildevice *)reg;
76 register i;
77
78COUNT(ILPROBE);
79#ifdef lint
80 br = 0; cvec = br; br = cvec;
81 ilrint(0); ilcint(0);
82#endif
83
84 addr->il_csr = ILC_OFFLINE|IL_CIE;
85 DELAY(100000);
86 i = addr->il_csr; /* Clear CDONE */
87 if (cvec > 0 && cvec != 0x200)
88 cvec -= 4;
89 return (1);
90}
91
92struct il_stat ilbuf;
93/*
94 * Interface exists: make available by filling in network interface
95 * record. System will initialize the interface when it is ready
96 * to accept packets. A STATUS command is done to get the ethernet
97 * address and other interesting data.
98 */
99ilattach(ui)
100 struct uba_device *ui;
101{
102 register struct il_softc *is = &il_softc[ui->ui_unit];
103 register struct sockaddr_in *sin;
104 register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
105 register int i;
106 int s;
107 int ubaddr;
108COUNT(ILATTACH);
109
110 is->is_if.if_unit = ui->ui_unit;
111 is->is_if.if_name = "il";
112 is->is_if.if_mtu = ILMTU;
113 is->is_if.if_net = ui->ui_flags & 0xff;
114
115 /*
116 * Reset the board
117 */
118 s = splimp();
119 addr->il_csr = ((ubaddr>>2)&0xc000)|ILC_RESET;
120 while (!(addr->il_csr & IL_CDONE))
121 /* Busy wait */;
122 if (addr->il_csr & IL_STATUS)
123 printf("il%d: %s\n", ui->ui_unit,
124 ildiag[addr->il_csr & IL_STATUS]);
125 splx(s);
126
127 /*
128 * Map the status buffer to the Unibus, do the status command,
129 * and unmap the buffer.
130 */
131 ubaddr = uballoc(ui->ui_ubanum, &ilbuf, sizeof(ilbuf), 0);
132 s = splimp();
133 addr->il_bar = ubaddr & 0xffff;
134 addr->il_bcr = sizeof(ilbuf);
135 addr->il_csr = ((ubaddr>>2)&0xc000)|ILC_STAT;
136 while (!(addr->il_csr & IL_CDONE))
137 /* Busy wait */;
138 if (addr->il_csr & IL_STATUS)
139 printf("il%d: %s\n", ui->ui_unit,
140 ilerrs[addr->il_csr & IL_STATUS]);
141 splx(s);
142 ubarelse(ui->ui_ubanum, &ubaddr);
143 /*
144 * Fill in the Ethernet address from the status buffer
145 */
146 for (i=0; i<6; i++)
147 is->is_enaddr[i] = ilbuf.ils_addr[i];
148 printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n",
149 ui->ui_unit,
150 is->is_enaddr[0]&0xff, is->is_enaddr[1]&0xff,
151 is->is_enaddr[2]&0xff, is->is_enaddr[3]&0xff,
152 is->is_enaddr[4]&0xff, is->is_enaddr[5]&0xff,
153 ilbuf.ils_module, ilbuf.ils_firmware);
154 is->is_if.if_host[0] = ((is->is_enaddr[3]&0xff)<<16) | 0x800000 |
155 ((is->is_enaddr[4]&0xff)<<8) | (is->is_enaddr[5]&0xff);
156 sin = (struct sockaddr_in *)&is->is_if.if_addr;
157 sin->sin_family = AF_INET;
158 sin->sin_addr = if_makeaddr(is->is_if.if_net, is->is_if.if_host[0]);
159
160 sin = (struct sockaddr_in *)&is->is_if.if_broadaddr;
161 sin->sin_family = AF_INET;
162 sin->sin_addr = if_makeaddr(is->is_if.if_net, 0);
163 is->is_if.if_flags = IFF_BROADCAST;
164
165 is->is_if.if_init = ilinit;
166 is->is_if.if_output = iloutput;
167 is->is_if.if_ubareset = ilreset;
168 is->is_ifuba.ifu_flags = UBA_CANTWAIT;
169 if_attach(&is->is_if);
170#if NIMP == 0
171 if (ui->ui_flags &~ 0xff)
dc39362e 172 illhinit(&is->is_if, (ui->ui_flags &~ 0xff) | 0x0a);
f0d51478
BF
173#endif
174}
175
176/*
177 * Reset of interface after UNIBUS reset.
178 * If interface is on specified uba, reset its state.
179 */
180ilreset(unit, uban)
181 int unit, uban;
182{
183 register struct uba_device *ui;
184COUNT(ILRESET);
185
186 if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 ||
187 ui->ui_ubanum != uban)
188 return;
189 printf(" il%d", unit);
190 ilinit(unit);
191}
192
193/*
194 * Initialization of interface; clear recorded pending
195 * operations, and reinitialize UNIBUS usage.
196 */
197ilinit(unit)
198 int unit;
199{
200 register struct il_softc *is = &il_softc[unit];
201 register struct uba_device *ui = ilinfo[unit];
202 register struct ildevice *addr;
203 register i;
204 int s;
205
206 if (if_ubainit(&is->is_ifuba, ui->ui_ubanum,
207 sizeof (struct il_rheader), (int)btoc(ILMTU)) == 0) {
208 printf("il%d: can't initialize\n", unit);
209 is->is_if.if_flags &= ~IFF_UP;
210 return;
211 }
212 addr = (struct ildevice *)ui->ui_addr;
213
214 /*
215 * Set board online.
216 * Hang receive buffer and start any pending
217 * writes by faking a transmit complete.
218 * Receive bcr is not a muliple of 4 so buffer
219 * chaining can't happen.
220 */
221 s = splimp();
222 addr->il_csr = ILC_ONLINE;
223 while (!(addr->il_csr & IL_CDONE))
224 /* Busy wait */;
225 addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
226 addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6;
227 addr->il_csr = ((is->is_ifuba.ifu_r.ifrw_info>>2)&0xc000)|
228 ILC_RCV|IL_RIE;
229 while (!(addr->il_csr & IL_CDONE))
230 /* Busy wait */;
231 is->is_startrcv = 0;
232 is->is_oactive = 1;
233 is->is_if.if_flags |= IFF_UP;
234 ilcint(unit);
235 splx(s);
236 if_rtinit(&is->is_if, RTF_DIRECT|RTF_UP);
237}
238
239/*
240 * Start output on interface.
241 * Get another datagram to send off of the interface queue,
242 * and map it to the interface before starting the output.
243 */
244ilstart(dev)
245 dev_t dev;
246{
247 int unit = ILUNIT(dev);
248 struct uba_device *ui = ilinfo[unit];
249 register struct il_softc *is = &il_softc[unit];
250 register struct ildevice *addr;
251 register len;
252 struct mbuf *m;
253 int dest;
254COUNT(ILSTART);
255
256 /*
257 * Dequeue another request and copy it into the buffer.
258 * If no more requests, just return.
259 */
260 IF_DEQUEUE(&is->is_if.if_snd, m);
261 if (m == 0)
262 return;
263 len = if_wubaput(&is->is_ifuba, m);
264
265 /*
266 * Flush BDP, then start the output.
267 */
268 if (is->is_ifuba.ifu_flags & UBA_NEEDBDP)
269 UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp);
270 addr = (struct ildevice *)ui->ui_addr;
271 addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff;
272 addr->il_bcr = len;
273 addr->il_csr = ((is->is_ifuba.ifu_w.ifrw_info>>2)&0xc000)|
274 ILC_XMIT|IL_CIE|IL_RIE;
275 is->is_oactive = 1;
276}
277
278/*
279 * Command done interrupt.
280 * This should only happen after a transmit command,
281 * so it is equivalent to a transmit interrupt.
282 * Start another output if more data to send.
283 */
284ilcint(unit)
285 int unit;
286{
287 register struct uba_device *ui = ilinfo[unit];
288 register struct il_softc *is = &il_softc[unit];
289 register struct ildevice *addr = (struct ildevice *)ui->ui_addr;
290 register int err;
291COUNT(ILCINT);
292
293 if (is->is_oactive == 0) {
294 printf("il%d: strange xmit interrupt!\n", unit);
295 return;
296 }
297 is->is_if.if_opackets++;
298 is->is_oactive = 0;
299 if (err = (addr->il_csr & IL_STATUS)){
300 is->is_if.if_oerrors++;
301 printf("il%d: output error %d\n", unit, err);
302 }
303 /*
304 * Hang receive buffer if it couldn't be done earlier (in ilrint).
305 */
306 if (is->is_startrcv) {
307 addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
308 addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6;
309 addr->il_csr = ((is->is_ifuba.ifu_r.ifrw_info>>2)&0xc000)|
310 ILC_RCV|IL_RIE;
311 while (!(addr->il_csr & IL_CDONE))
312 /* Busy wait */;
313 is->is_startrcv = 0;
314 }
315 if (is->is_ifuba.ifu_xtofree) {
316 m_freem(is->is_ifuba.ifu_xtofree);
317 is->is_ifuba.ifu_xtofree = 0;
318 }
319 if (is->is_if.if_snd.ifq_head == 0) {
320 return;
321 }
322 ilstart(unit);
323}
324
325/*
326 * Ethernet interface receiver interrupt.
327 * If input error just drop packet.
328 * Otherwise purge input buffered data path and examine
329 * packet to determine type. If can't determine length
330 * from type, then have to drop packet. Othewise decapsulate
331 * packet based on type and pass to type specific higher-level
332 * input routine.
333 */
334ilrint(unit)
335 int unit;
336{
337 register struct il_softc *is = &il_softc[unit];
338 struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr;
339 register struct il_rheader *il;
340 struct mbuf *m;
341 int len, off, resid;
342 register struct ifqueue *inq;
343
344COUNT(ILRINT);
345 is->is_if.if_ipackets++;
346 if (is->is_ifuba.ifu_flags & UBA_NEEDBDP)
347 UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp);
348 il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr);
349 len = il->ilr_length - sizeof(struct il_rheader);
350 if (il->ilr_status&0x3 || len < 46 || len > ILMTU) {
351 is->is_if.if_ierrors++;
352#ifdef notdef
353 if (is->is_if.if_ierrors % 100 == 0)
354 printf("il%d: += 100 input errors\n", unit);
355#endif
356 printf("il%d: input error (status=%x, len=%d)\n", unit,
357 il->ilr_status, len);
358 goto setup;
359 }
360
361 /*
362 * Deal with trailer protocol: if type is PUP trailer
363 * get true type from first 16-bit word past data.
364 * Remember that type was trailer by setting off.
365 */
366#define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off))))
367 if (il->ilr_type >= ILPUP_TRAIL &&
368 il->ilr_type < ILPUP_TRAIL+ILPUP_NTRAILER) {
369 off = (il->ilr_type - ILPUP_TRAIL) * 512;
370 if (off >= ILMTU)
371 goto setup; /* sanity */
372 il->ilr_type = *ildataaddr(il, off, u_short *);
373 resid = *(ildataaddr(il, off+2, u_short *));
374 if (off + resid > len)
375 goto setup; /* sanity */
376 len = off + resid;
377 } else
378 off = 0;
379 if (len == 0)
380 goto setup;
381
382 /*
383 * Pull packet off interface. Off is nonzero if packet
384 * has trailing header; ilget will then force this header
385 * information to be at the front, but we still have to drop
386 * the type and length which are at the front of any trailer data.
387 */
388 m = if_rubaget(&is->is_ifuba, len, off);
389 if (m == 0)
390 goto setup;
391 if (off) {
392 m->m_off += 2 * sizeof (u_short);
393 m->m_len -= 2 * sizeof (u_short);
394 }
395 switch (il->ilr_type) {
396
397#ifdef INET
398 case ILPUP_IPTYPE:
399 schednetisr(NETISR_IP);
400 inq = &ipintrq;
401 break;
402#endif
403 default:
404 m_freem(m);
405 goto setup;
406 }
407
408 if (IF_QFULL(inq)) {
409 IF_DROP(inq);
410 m_freem(m);
411 } else
412 IF_ENQUEUE(inq, m);
413
414setup:
415 /*
416 * Reset for next packet if possible.
417 * If waiting for transmit command completion, set flag
418 * and wait until command completes.
419 */
420 if (!is->is_oactive) {
421 addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff;
422 addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6;
423 addr->il_csr = ((is->is_ifuba.ifu_r.ifrw_info>>2)&0xc000)|
424 ILC_RCV|IL_RIE;
425 while (!(addr->il_csr & IL_CDONE))
426 /* Busy wait */;
427 } else
428 is->is_startrcv = 1;
429}
430
431/*
432 * Ethernet output routine.
433 * Encapsulate a packet of type family for the local net.
434 * Use trailer local net encapsulation if enough data in first
435 * packet leaves a multiple of 512 bytes of data in remainder.
436 */
437iloutput(ifp, m0, dst)
438 struct ifnet *ifp;
439 struct mbuf *m0;
440 struct sockaddr *dst;
441{
442 int type, dest, s, error;
443 register struct il_softc *is = &il_softc[ifp->if_unit];
444 register struct mbuf *m = m0;
445 register struct il_xheader *il;
446 register int off;
447 register int i;
448
449COUNT(ILOUTPUT);
450 switch (dst->sa_family) {
451
452#ifdef INET
453 case AF_INET:
454 dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
455 off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
456 if (off > 0 && (off & 0x1ff) == 0 &&
457 m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
458 type = ILPUP_TRAIL + (off>>9);
459 m->m_off -= 2 * sizeof (u_short);
460 m->m_len += 2 * sizeof (u_short);
461 *mtod(m, u_short *) = ILPUP_IPTYPE;
462 *(mtod(m, u_short *) + 1) = m->m_len;
463 goto gottrailertype;
464 }
465 type = ILPUP_IPTYPE;
466 off = 0;
467 goto gottype;
468#endif
469
470 default:
471 printf("il%d: can't handle af%d\n", ifp->if_unit,
472 dst->sa_family);
473 error = EAFNOSUPPORT;
474 goto bad;
475 }
476
477gottrailertype:
478 /*
479 * Packet to be sent as trailer: move first packet
480 * (control information) to end of chain.
481 */
482 while (m->m_next)
483 m = m->m_next;
484 m->m_next = m0;
485 m = m0->m_next;
486 m0->m_next = 0;
487 m0 = m;
488
489gottype:
490 /*
491 * Add local net header. If no space in first mbuf,
492 * allocate another.
493 */
494 if (m->m_off > MMAXOFF ||
495 MMINOFF + sizeof (struct il_xheader) > m->m_off) {
496 m = m_get(M_DONTWAIT);
497 if (m == 0) {
498 error = ENOBUFS;
499 goto bad;
500 }
501 m->m_next = m0;
502 m->m_off = MMINOFF;
503 m->m_len = sizeof (struct il_xheader);
504 } else {
505 m->m_off -= sizeof (struct il_xheader);
506 m->m_len += sizeof (struct il_xheader);
507 }
508 il = mtod(m, struct il_xheader *);
509 if ((dest &~ 0xff) == 0)
510 for (i=0; i<6; i++)
511 il->ilx_dhost[i] = 0xff;
512 else {
513 if (dest & 0x8000) {
514 il->ilx_dhost[0] = is->is_enaddr[0];
515 il->ilx_dhost[1] = is->is_enaddr[1];
516 il->ilx_dhost[2] = is->is_enaddr[2];
517 } else {
518 il->ilx_dhost[0] = il_ectop[0];
519 il->ilx_dhost[1] = il_ectop[1];
520 il->ilx_dhost[2] = il_ectop[2];
521 }
522 il->ilx_dhost[3] = (dest>>8) & 0x7f;
523 il->ilx_dhost[4] = (dest>>16) & 0xff;
524 il->ilx_dhost[5] = (dest>>24) & 0xff;
525 }
526 il->ilx_type = type;
527
528 /*
529 * Queue message on interface, and start output if interface
530 * not yet active.
531 */
532 s = splimp();
533 if (IF_QFULL(&ifp->if_snd)) {
534 IF_DROP(&ifp->if_snd);
535 error = ENOBUFS;
536 goto qfull;
537 }
538 IF_ENQUEUE(&ifp->if_snd, m);
539 if (is->is_oactive == 0)
540 ilstart(ifp->if_unit);
541 splx(s);
542 return (0);
543qfull:
544 m0 = m;
545 splx(s);
546bad:
547 m_freem(m0);
548 return(error);
549}
550
551#if NIMP == 0 && NIL > 0
552/*
553 * Logical host interface driver.
554 * Allows host to appear as an ARPAnet
555 * logical host. Must also have routing
556 * table entry set up to forward packets
557 * to appropriate gateway on localnet.
558 */
559
560struct ifnet illhif;
dc39362e 561int looutput();
f0d51478
BF
562
563/*
564 * Called by localnet interface to allow logical
565 * host interface to "attach". Nothing should ever
566 * be sent locally to this interface, it's purpose
567 * is simply to establish the host's arpanet address.
568 */
dc39362e
BJ
569illhinit(ilifp, addr)
570 struct ifnet *ilifp;
f0d51478
BF
571 int addr;
572{
573 register struct ifnet *ifp = &illhif;
574 register struct sockaddr_in *sin;
575
576COUNT(ILLHINIT);
577 ifp->if_name = "lh";
578 ifp->if_mtu = ILMTU;
579 sin = (struct sockaddr_in *)&ifp->if_addr;
580 sin->sin_family = AF_INET;
581 sin->sin_addr.s_addr = addr;
34a8182d 582 sin->sin_addr.s_lh = ilifp->if_host[0];
f0d51478 583 ifp->if_net = sin->sin_addr.s_net;
34a8182d 584 ifp->if_dstaddr = ifp->if_addr;
dc39362e
BJ
585 ifp->if_flags = IFF_UP|IFF_POINTOPOINT;
586 ifp->if_output = looutput;
f0d51478 587 if_attach(ifp);
c46ba114 588 rtinit(&ifp->if_addr, &ifp->if_addr, RTF_UP|RTF_DIRECT|RTF_HOST);
f0d51478
BF
589}
590#endif