trailer's handled wrong on pup's
[unix-history] / usr / src / sys / vax / if / if_en.c
CommitLineData
80a4458b 1/* if_en.c 4.46 82/03/30 */
11720282
BJ
2
3#include "en.h"
f1b2fa5b 4
11720282 5/*
f1b2fa5b 6 * Xerox prototype (3 Mb) Ethernet interface driver.
11720282
BJ
7 */
8
9#include "../h/param.h"
10#include "../h/systm.h"
11#include "../h/mbuf.h"
11720282
BJ
12#include "../h/pte.h"
13#include "../h/buf.h"
8a13b737
BJ
14#include "../h/protosw.h"
15#include "../h/socket.h"
11720282
BJ
16#include "../h/ubareg.h"
17#include "../h/ubavar.h"
11720282 18#include "../h/enreg.h"
11720282 19#include "../h/cpu.h"
8a13b737
BJ
20#include "../h/mtpr.h"
21#include "../h/vmmac.h"
22#include "../net/in.h"
23#include "../net/in_systm.h"
24#include "../net/if.h"
25#include "../net/if_en.h"
26#include "../net/if_uba.h"
27#include "../net/ip.h"
28#include "../net/ip_var.h"
31c2345c 29#include "../net/pup.h"
f6311fb6 30#include "../net/route.h"
8a13b737 31
a453e1b5 32#define ENMTU (1024+512)
11720282 33
11720282
BJ
34int enprobe(), enattach(), enrint(), enxint(), encollide();
35struct uba_device *eninfo[NEN];
36u_short enstd[] = { 0 };
37struct uba_driver endriver =
b454c3ea 38 { enprobe, 0, enattach, 0, enstd, "en", eninfo };
11720282
BJ
39#define ENUNIT(x) minor(x)
40
f1b2fa5b
BJ
41int eninit(),enoutput(),enreset();
42
43/*
44 * Ethernet software status per interface.
45 *
46 * Each interface is referenced by a network interface structure,
47 * es_if, which the routing code uses to locate the interface.
48 * This structure contains the output queue for the interface, its address, ...
49 * We also have, for each interface, a UBA interface structure, which
50 * contains information about the UNIBUS resources held by the interface:
51 * map registers, buffered data paths, etc. Information is cached in this
52 * structure for use by the if_uba.c routines in running the interface
53 * efficiently.
54 */
8a13b737 55struct en_softc {
f1b2fa5b
BJ
56 struct ifnet es_if; /* network-visible interface */
57 struct ifuba es_ifuba; /* UNIBUS resources */
58 short es_delay; /* current output delay */
59 short es_mask; /* mask for current output delay */
60 u_char es_lastx; /* host last transmitted to */
61 short es_oactive; /* is output active? */
62 short es_olen; /* length of last output */
8a13b737 63} en_softc[NEN];
11720282 64
f1b2fa5b
BJ
65/*
66 * Do output DMA to determine interface presence and
67 * interrupt vector. DMA is too short to disturb other hosts.
68 */
11720282
BJ
69enprobe(reg)
70 caddr_t reg;
71{
b454c3ea 72 register int br, cvec; /* r11, r10 value-result */
11720282
BJ
73 register struct endevice *addr = (struct endevice *)reg;
74
8a13b737 75COUNT(ENPROBE);
11720282
BJ
76#ifdef lint
77 br = 0; cvec = br; br = cvec;
2b4b57cd 78 enrint(0); enxint(0); encollide(0);
11720282 79#endif
11720282 80 addr->en_istat = 0;
11720282
BJ
81 addr->en_owc = -1;
82 addr->en_oba = 0;
941ee7ef 83 addr->en_ostat = EN_IEN|EN_GO;
11720282
BJ
84 DELAY(100000);
85 addr->en_ostat = 0;
11720282
BJ
86 return (1);
87}
88
f1b2fa5b
BJ
89/*
90 * Interface exists: make available by filling in network interface
91 * record. System will initialize the interface when it is ready
92 * to accept packets.
93 */
11720282
BJ
94enattach(ui)
95 struct uba_device *ui;
96{
f1b2fa5b 97 register struct en_softc *es = &en_softc[ui->ui_unit];
ee787340 98 register struct sockaddr_in *sin;
8a13b737 99COUNT(ENATTACH);
f1b2fa5b
BJ
100
101 es->es_if.if_unit = ui->ui_unit;
b454c3ea 102 es->es_if.if_name = "en";
f1b2fa5b
BJ
103 es->es_if.if_mtu = ENMTU;
104 es->es_if.if_net = ui->ui_flags;
105 es->es_if.if_host[0] =
ee787340
SL
106 (~(((struct endevice *)eninfo[ui->ui_unit]->ui_addr)->en_addr)) & 0xff;
107 sin = (struct sockaddr_in *)&es->es_if.if_addr;
108 sin->sin_family = AF_INET;
109 sin->sin_addr = if_makeaddr(es->es_if.if_net, es->es_if.if_host[0]);
110 sin = (struct sockaddr_in *)&es->es_if.if_broadaddr;
111 sin->sin_family = AF_INET;
112 sin->sin_addr = if_makeaddr(es->es_if.if_net, 0);
113 es->es_if.if_flags = IFF_BROADCAST;
f1b2fa5b 114 es->es_if.if_init = eninit;
b454c3ea 115 es->es_if.if_output = enoutput;
f1b2fa5b 116 es->es_if.if_ubareset = enreset;
7a66118b 117 es->es_ifuba.ifu_flags = UBA_NEEDBDP | UBA_NEED16;
405c9168 118 if_attach(&es->es_if);
11720282
BJ
119}
120
f1b2fa5b
BJ
121/*
122 * Reset of interface after UNIBUS reset.
123 * If interface is on specified uba, reset its state.
124 */
125enreset(unit, uban)
126 int unit, uban;
8a13b737 127{
11720282 128 register struct uba_device *ui;
f1b2fa5b 129COUNT(ENRESET);
11720282 130
b454c3ea
BJ
131 if (unit >= NEN || (ui = eninfo[unit]) == 0 || ui->ui_alive == 0 ||
132 ui->ui_ubanum != uban)
f1b2fa5b 133 return;
b454c3ea 134 printf(" en%d", unit);
f1b2fa5b
BJ
135 eninit(unit);
136}
137
138/*
139 * Initialization of interface; clear recorded pending
140 * operations, and reinitialize UNIBUS usage.
141 */
142eninit(unit)
143 int unit;
144{
b454c3ea
BJ
145 register struct en_softc *es = &en_softc[unit];
146 register struct uba_device *ui = eninfo[unit];
f1b2fa5b 147 register struct endevice *addr;
f1b2fa5b
BJ
148 int s;
149
f1b2fa5b 150 if (if_ubainit(&es->es_ifuba, ui->ui_ubanum,
81950b45 151 sizeof (struct en_header), (int)btoc(ENMTU)) == 0) {
b454c3ea 152 printf("en%d: can't initialize\n", unit);
ee787340 153 es->es_if.if_flags &= ~IFF_UP;
8a13b737 154 return;
11720282 155 }
11720282 156 addr = (struct endevice *)ui->ui_addr;
8a13b737 157 addr->en_istat = addr->en_ostat = 0;
11720282 158
f1b2fa5b 159 /*
b454c3ea
BJ
160 * Hang a receive and start any
161 * pending writes by faking a transmit complete.
f1b2fa5b
BJ
162 */
163 s = splimp();
b454c3ea
BJ
164 addr->en_iba = es->es_ifuba.ifu_r.ifrw_info;
165 addr->en_iwc = -(sizeof (struct en_header) + ENMTU) >> 1;
166 addr->en_istat = EN_IEN|EN_GO;
167 es->es_oactive = 1;
ee787340 168 es->es_if.if_flags |= IFF_UP;
f1b2fa5b
BJ
169 enxint(unit);
170 splx(s);
f6311fb6 171 if_rtinit(&es->es_if, RTF_DIRECT|RTF_UP);
11720282
BJ
172}
173
3552a746 174int enlastdel = 25;
8a13b737 175
f1b2fa5b
BJ
176/*
177 * Start or restart output on interface.
178 * If interface is already active, then this is a retransmit
179 * after a collision, and just restuff registers and delay.
180 * If interface is not already active, get another datagram
181 * to send off of the interface queue, and map it to the interface
182 * before starting the output.
183 */
ae348eea 184enstart(dev)
11720282
BJ
185 dev_t dev;
186{
b454c3ea
BJ
187 int unit = ENUNIT(dev);
188 struct uba_device *ui = eninfo[unit];
189 register struct en_softc *es = &en_softc[unit];
8a13b737 190 register struct endevice *addr;
8a13b737
BJ
191 struct mbuf *m;
192 int dest;
ae348eea 193COUNT(ENSTART);
11720282 194
8a13b737
BJ
195 if (es->es_oactive)
196 goto restart;
f1b2fa5b
BJ
197
198 /*
199 * Not already active: dequeue another request
200 * and map it to the UNIBUS. If no more requests,
201 * just return.
202 */
203 IF_DEQUEUE(&es->es_if.if_snd, m);
8a13b737
BJ
204 if (m == 0) {
205 es->es_oactive = 0;
11720282
BJ
206 return;
207 }
a453e1b5 208 dest = mtod(m, struct en_header *)->en_dhost;
8a13b737 209 es->es_olen = if_wubaput(&es->es_ifuba, m);
f1b2fa5b
BJ
210
211 /*
212 * Ethernet cannot take back-to-back packets (no
213 * buffering in interface. To avoid overrunning
214 * receiver, enforce a small delay (about 1ms) in interface
215 * on successive packets sent to same host.
216 */
8a13b737
BJ
217 if (es->es_lastx && es->es_lastx == dest)
218 es->es_delay = enlastdel;
219 else
220 es->es_lastx = dest;
f1b2fa5b 221
8a13b737 222restart:
f1b2fa5b
BJ
223 /*
224 * Have request mapped to UNIBUS for transmission.
225 * Purge any stale data from this BDP, and start the otput.
226 */
ee787340
SL
227 if (es->es_ifuba.ifu_flags & UBA_NEEDBDP)
228 UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_w.ifrw_bdp);
11720282 229 addr = (struct endevice *)ui->ui_addr;
405c9168 230 addr->en_oba = (int)es->es_ifuba.ifu_w.ifrw_info;
8a13b737
BJ
231 addr->en_odelay = es->es_delay;
232 addr->en_owc = -((es->es_olen + 1) >> 1);
941ee7ef 233 addr->en_ostat = EN_IEN|EN_GO;
8a13b737 234 es->es_oactive = 1;
11720282
BJ
235}
236
f1b2fa5b
BJ
237/*
238 * Ethernet interface transmitter interrupt.
239 * Start another output if more data to send.
240 */
11720282
BJ
241enxint(unit)
242 int unit;
243{
b454c3ea
BJ
244 register struct uba_device *ui = eninfo[unit];
245 register struct en_softc *es = &en_softc[unit];
519a1ecf 246 register struct endevice *addr = (struct endevice *)ui->ui_addr;
11720282
BJ
247COUNT(ENXINT);
248
8a13b737
BJ
249 if (es->es_oactive == 0)
250 return;
519a1ecf 251 if (es->es_mask && (addr->en_ostat&EN_OERROR)) {
89413846 252 es->es_if.if_oerrors++;
4020bf24
BJ
253 if (es->es_if.if_oerrors % 100 == 0)
254 printf("en%d: += 100 output errors\n", unit);
519a1ecf
BJ
255 endocoll(unit);
256 return;
89413846 257 }
519a1ecf
BJ
258 es->es_if.if_opackets++;
259 es->es_oactive = 0;
260 es->es_delay = 0;
261 es->es_mask = ~0;
7a66118b
BJ
262 if (es->es_ifuba.ifu_xtofree) {
263 m_freem(es->es_ifuba.ifu_xtofree);
264 es->es_ifuba.ifu_xtofree = 0;
265 }
f1b2fa5b 266 if (es->es_if.if_snd.ifq_head == 0) {
8a13b737 267 es->es_lastx = 0;
11720282
BJ
268 return;
269 }
8a13b737 270 enstart(unit);
11720282
BJ
271}
272
f1b2fa5b
BJ
273/*
274 * Collision on ethernet interface. Do exponential
275 * backoff, and retransmit. If have backed off all
ee787340 276 * the way print warning diagnostic, and drop packet.
f1b2fa5b 277 */
11720282
BJ
278encollide(unit)
279 int unit;
280{
519a1ecf 281 struct en_softc *es = &en_softc[unit];
11720282
BJ
282COUNT(ENCOLLIDE);
283
f1b2fa5b 284 es->es_if.if_collisions++;
8a13b737 285 if (es->es_oactive == 0)
11720282 286 return;
519a1ecf
BJ
287 endocoll(unit);
288}
289
290endocoll(unit)
291 int unit;
292{
293 register struct en_softc *es = &en_softc[unit];
294
b454c3ea
BJ
295 /*
296 * Es_mask is a 16 bit number with n low zero bits, with
297 * n the number of backoffs. When es_mask is 0 we have
298 * backed off 16 times, and give up.
299 */
8a13b737 300 if (es->es_mask == 0) {
a453e1b5 301 printf("en%d: send error\n", unit);
8a13b737 302 enxint(unit);
b454c3ea 303 return;
11720282 304 }
b454c3ea
BJ
305 /*
306 * Another backoff. Restart with delay based on n low bits
307 * of the interval timer.
308 */
309 es->es_mask <<= 1;
310 es->es_delay = mfpr(ICR) &~ es->es_mask;
311 enstart(unit);
11720282
BJ
312}
313
31c2345c
SL
314struct sockaddr_pup pupsrc = { AF_PUP };
315struct sockaddr_pup pupdst = { AF_PUP };
316struct sockproto pupproto = { PF_PUP };
f1b2fa5b
BJ
317/*
318 * Ethernet interface receiver interrupt.
319 * If input error just drop packet.
320 * Otherwise purge input buffered data path and examine
321 * packet to determine type. If can't determine length
322 * from type, then have to drop packet. Othewise decapsulate
323 * packet based on type and pass to type specific higher-level
324 * input routine.
325 */
11720282
BJ
326enrint(unit)
327 int unit;
328{
b454c3ea
BJ
329 register struct en_softc *es = &en_softc[unit];
330 struct endevice *addr = (struct endevice *)eninfo[unit]->ui_addr;
331 register struct en_header *en;
8a13b737 332 struct mbuf *m;
b454c3ea
BJ
333 int len;
334 register struct ifqueue *inq;
8a13b737 335 int off;
11720282
BJ
336COUNT(ENRINT);
337
b454c3ea 338 es->es_if.if_ipackets++;
f1b2fa5b
BJ
339
340 /*
b454c3ea 341 * Purge BDP; drop if input error indicated.
f1b2fa5b 342 */
ee787340
SL
343 if (es->es_ifuba.ifu_flags & UBA_NEEDBDP)
344 UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_r.ifrw_bdp);
941ee7ef 345 if (addr->en_istat&EN_IERROR) {
f1b2fa5b 346 es->es_if.if_ierrors++;
4020bf24
BJ
347 if (es->es_if.if_ierrors % 100 == 0)
348 printf("en%d: += 100 input errors\n", unit);
8a13b737 349 goto setup;
11720282 350 }
f1b2fa5b
BJ
351
352 /*
353 * Get pointer to ethernet header (in input buffer).
354 * Deal with trailer protocol: if type is PUP trailer
355 * get true type from first 16-bit word past data.
356 * Remember that type was trailer by setting off.
357 */
358 en = (struct en_header *)(es->es_ifuba.ifu_r.ifrw_addr);
8a13b737
BJ
359#define endataaddr(en, off, type) ((type)(((caddr_t)((en)+1)+(off))))
360 if (en->en_type >= ENPUP_TRAIL &&
361 en->en_type < ENPUP_TRAIL+ENPUP_NTRAILER) {
362 off = (en->en_type - ENPUP_TRAIL) * 512;
b454c3ea
BJ
363 if (off >= ENMTU)
364 goto setup; /* sanity */
8a13b737 365 en->en_type = *endataaddr(en, off, u_short *);
8a13b737
BJ
366 } else
367 off = 0;
f1b2fa5b
BJ
368
369 /*
370 * Attempt to infer packet length from type;
371 * can't deal with packet if can't infer length.
372 */
8a13b737
BJ
373 switch (en->en_type) {
374
375#ifdef INET
376 case ENPUP_IPTYPE:
ee787340
SL
377 len = htons((u_short)endataaddr(en,
378 off ? off + sizeof (u_short) : 0, struct ip *)->ip_len);
11720282
BJ
379 break;
380#endif
31c2345c
SL
381#ifdef PUP
382 case ENPUP_PUPTYPE:
ee787340
SL
383 len = endataaddr(en, off ? off + sizeof (u_short) : 0,
384 struct pup_header *)->pup_length;
31c2345c
SL
385 break;
386#endif
387
8a13b737 388 default:
5d855bdf 389 printf("en%d: unknown pkt type 0x%x\n", unit, en->en_type);
8a13b737 390 goto setup;
11720282 391 }
80a4458b
SL
392 if (off)
393 len += sizeof (u_short);
8a13b737
BJ
394 if (len == 0)
395 goto setup;
f1b2fa5b
BJ
396
397 /*
398 * Pull packet off interface. Off is nonzero if packet
399 * has trailing header; if_rubaget will then force this header
400 * information to be at the front, but we still have to drop
b454c3ea 401 * the two-byte type which is at the front of any trailer data.
f1b2fa5b
BJ
402 */
403 m = if_rubaget(&es->es_ifuba, len, off);
a453e1b5
BJ
404 if (m == 0)
405 goto setup;
f1b2fa5b 406 if (off) {
ee787340
SL
407 m->m_off += sizeof (u_short);
408 m->m_len -= sizeof (u_short);
f1b2fa5b 409 }
31c2345c
SL
410 switch (en->en_type) {
411
412#ifdef INET
413 case ENPUP_IPTYPE:
9c8692e9 414 schednetisr(NETISR_IP);
31c2345c
SL
415 inq = &ipintrq;
416 break;
417#endif
ee787340 418#ifdef PUP
31c2345c
SL
419 case ENPUP_PUPTYPE: {
420 struct pup_header *pup = mtod(m, struct pup_header *);
421
422 pupproto.sp_protocol = pup->pup_type;
423 pupdst.spup_addr = pup->pup_dport;
424 pupsrc.spup_addr = pup->pup_sport;
668cc26d 425 raw_input(m, &pupproto, (struct sockaddr *)&pupdst,
ee787340 426 (struct sockaddr *)&pupsrc);
31c2345c 427 goto setup;
31c2345c 428 }
ee787340
SL
429#endif
430 }
431
1e977657
BJ
432 if (IF_QFULL(inq)) {
433 IF_DROP(inq);
ee787340 434 m_freem(m);
1e977657
BJ
435 } else
436 IF_ENQUEUE(inq, m);
f1b2fa5b 437
ae348eea 438setup:
f1b2fa5b
BJ
439 /*
440 * Reset for next packet.
441 */
442 addr->en_iba = es->es_ifuba.ifu_r.ifrw_info;
8a13b737 443 addr->en_iwc = -(sizeof (struct en_header) + ENMTU) >> 1;
941ee7ef 444 addr->en_istat = EN_IEN|EN_GO;
ae348eea 445}
11720282 446
8a13b737
BJ
447/*
448 * Ethernet output routine.
449 * Encapsulate a packet of type family for the local net.
f1b2fa5b
BJ
450 * Use trailer local net encapsulation if enough data in first
451 * packet leaves a multiple of 512 bytes of data in remainder.
8a13b737 452 */
ee787340 453enoutput(ifp, m0, dst)
8a13b737
BJ
454 struct ifnet *ifp;
455 struct mbuf *m0;
ee787340 456 struct sockaddr *dst;
11720282 457{
ee787340 458 int type, dest, s;
f1b2fa5b 459 register struct mbuf *m = m0;
8a13b737 460 register struct en_header *en;
ee787340 461 register int off;
8a13b737 462
9d1b03e0 463COUNT(ENOUTPUT);
ee787340 464 switch (dst->sa_family) {
11720282 465
8a13b737 466#ifdef INET
ee787340
SL
467 case AF_INET:
468 dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr >> 24;
469 off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
470 if (off > 0 && (off & 0x1ff) == 0 &&
471 m->m_off >= MMINOFF + sizeof (u_short)) {
8a13b737 472 type = ENPUP_TRAIL + (off>>9);
ee787340
SL
473 m->m_off -= sizeof (u_short);
474 m->m_len += sizeof (u_short);
f1b2fa5b
BJ
475 *mtod(m, u_short *) = ENPUP_IPTYPE;
476 goto gottrailertype;
8a13b737 477 }
f1b2fa5b
BJ
478 type = ENPUP_IPTYPE;
479 off = 0;
480 goto gottype;
8a13b737 481#endif
31c2345c 482#ifdef PUP
ee787340
SL
483 case AF_PUP:
484 dest = ((struct sockaddr_pup *)dst)->spup_addr.pp_host;
485 off = mtod(m, struct pup_header *)->pup_length - m->m_len;
486 if (off > 0 && (off & 0x1ff) == 0 &&
487 m->m_off >= MMINOFF + sizeof (u_short)) {
31c2345c 488 type = ENPUP_TRAIL + (off>>9);
ee787340
SL
489 m->m_off -= sizeof (u_short);
490 m->m_len += sizeof (u_short);
31c2345c
SL
491 *mtod(m, u_short *) = ENPUP_PUPTYPE;
492 goto gottrailertype;
493 }
494 type = ENPUP_PUPTYPE;
495 off = 0;
496 goto gottype;
31c2345c 497#endif
8a13b737
BJ
498
499 default:
ee787340
SL
500 printf("en%d: can't handle af%d\n", ifp->if_unit,
501 dst->sa_family);
8a13b737
BJ
502 m_freem(m0);
503 return (0);
504 }
f1b2fa5b 505
b454c3ea 506gottrailertype:
f1b2fa5b
BJ
507 /*
508 * Packet to be sent as trailer: move first packet
509 * (control information) to end of chain.
510 */
f1b2fa5b
BJ
511 while (m->m_next)
512 m = m->m_next;
513 m->m_next = m0;
514 m = m0->m_next;
515 m0->m_next = 0;
b454c3ea 516 m0 = m;
f1b2fa5b 517
b454c3ea 518gottype:
f1b2fa5b
BJ
519 /*
520 * Add local net header. If no space in first mbuf,
521 * allocate another.
522 */
a453e1b5
BJ
523 if (m->m_off > MMAXOFF ||
524 MMINOFF + sizeof (struct en_header) > m->m_off) {
ef9b4258 525 m = m_get(M_DONTWAIT);
8a13b737
BJ
526 if (m == 0) {
527 m_freem(m0);
528 return (0);
529 }
530 m->m_next = m0;
531 m->m_off = MMINOFF;
532 m->m_len = sizeof (struct en_header);
533 } else {
8a13b737
BJ
534 m->m_off -= sizeof (struct en_header);
535 m->m_len += sizeof (struct en_header);
11720282 536 }
8a13b737
BJ
537 en = mtod(m, struct en_header *);
538 en->en_shost = ifp->if_host[0];
539 en->en_dhost = dest;
540 en->en_type = type;
f1b2fa5b
BJ
541
542 /*
543 * Queue message on interface, and start output if interface
544 * not yet active.
545 */
8a13b737 546 s = splimp();
1e977657
BJ
547 if (IF_QFULL(&ifp->if_snd)) {
548 IF_DROP(&ifp->if_snd);
549 m_freem(m);
550 splx(s);
551 return (0);
552 }
8a13b737 553 IF_ENQUEUE(&ifp->if_snd, m);
8a13b737
BJ
554 if (en_softc[ifp->if_unit].es_oactive == 0)
555 enstart(ifp->if_unit);
89413846 556 splx(s);
f1b2fa5b 557 return (1);
11720282 558}