lint
[unix-history] / usr / src / sys / vax / if / if_ex.c
CommitLineData
da7c5cc6
KM
1/*
2 * Copyright (c) 1982 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 *
8011f5df 6 * @(#)if_ex.c 6.14 (Berkeley) %G%
da7c5cc6
KM
7 */
8
063e82dc
MK
9
10#include "ex.h"
9a0b0c74 11#if NEX > 0
063e82dc
MK
12
13/*
14 * Excelan EXOS 204 Interface
15 *
16 * George Powers
17 * Excelan Inc.
18 */
19
20#include "../machine/pte.h"
21
1a568327
MK
22#include "param.h"
23#include "systm.h"
24#include "mbuf.h"
25#include "buf.h"
26#include "protosw.h"
27#include "socket.h"
28#include "vmmac.h"
29#include "ioctl.h"
30#include "errno.h"
063e82dc
MK
31
32#include "../net/if.h"
33#include "../net/netisr.h"
34#include "../net/route.h"
7bdbbe60 35
7bdbbe60 36#ifdef INET
063e82dc
MK
37#include "../netinet/in.h"
38#include "../netinet/in_systm.h"
1a568327 39#include "../netinet/in_var.h"
063e82dc 40#include "../netinet/ip.h"
063e82dc 41#include "../netinet/if_ether.h"
7bdbbe60
MK
42#endif
43
f2803948
KS
44#ifdef NS
45#include "../netns/ns.h"
46#include "../netns/ns_if.h"
47#endif
48
063e82dc
MK
49#include "../vax/cpu.h"
50#include "../vax/mtpr.h"
1a568327
MK
51#include "if_exreg.h"
52#include "if_uba.h"
063e82dc
MK
53#include "../vaxuba/ubareg.h"
54#include "../vaxuba/ubavar.h"
55
56#define DEBUG /* check for "impossible" events */
57
58#define NH2X 4 /* a sufficient number is critical */
59#define NX2H 4 /* this is pretty arbitrary */
60#define EXWATCHINTVL 10 /* call exwatch() every 10 seconds */
61
62int exprobe(), exattach(), excdint();
63struct uba_device *exinfo[NEX];
64u_short exstd[] = { 0 };
65struct uba_driver exdriver =
66 { exprobe, 0, exattach, 0, exstd, "ex", exinfo };
67int exinit(),exoutput(),exioctl(),exreset(),exwatch();
68struct ex_msg *exgetcbuf();
69
70/*
71 * Ethernet software status per interface.
72 *
73 * Each interface is referenced by a network interface structure,
74 * xs_if, which the routing code uses to locate the interface.
75 * This structure contains the output queue for the interface, its address, ...
76 * We also have, for each interface, a UBA interface structure, which
77 * contains information about the UNIBUS resources held by the interface:
78 * map registers, buffered data paths, etc. Information is cached in this
79 * structure for use by the if_uba.c routines in running the interface
80 * efficiently.
81 */
82struct ex_softc {
063e82dc
MK
83 struct arpcom xs_ac; /* Ethernet common part */
84#define xs_if xs_ac.ac_if /* network-visible interface */
85#define xs_addr xs_ac.ac_enaddr /* hardware Ethernet address */
1a568327
MK
86#ifdef DEBUG
87 int xs_wait;
88#endif
063e82dc
MK
89 struct ifuba xs_ifuba; /* UNIBUS resources */
90 int xs_flags; /* private flags */
91#define EX_XPENDING 1 /* xmit rqst pending on EXOS */
92#define EX_STATPENDING (1<<1) /* stats rqst pending on EXOS */
0f487295 93#define EX_RUNNING (1<<2) /* board is running */
be85b93a 94#define EX_SETADDR (1<<3) /* physaddr has been changed */
063e82dc
MK
95 struct ex_msg *xs_h2xnext; /* host pointer to request queue */
96 struct ex_msg *xs_x2hnext; /* host pointer to reply queue */
8011f5df 97 int xs_ubaddr; /* map info for structs below */
063e82dc
MK
98#define UNIADDR(x) ((u_long)(x)&0x3FFFF)
99#define P_UNIADDR(x) ((u_long)(x)&0x3FFF0)
100 /* the following structures are always mapped in */
101 u_short xs_h2xhdr; /* EXOS's request queue header */
102 u_short xs_x2hhdr; /* EXOS's reply queue header */
103 struct ex_msg xs_h2xent[NH2X]; /* request msg buffers */
104 struct ex_msg xs_x2hent[NX2H]; /* reply msg buffers */
105 struct confmsg xs_cm; /* configuration message */
106 struct stat_array xs_xsa; /* EXOS writes stats here */
107 /* end mapped area */
8011f5df 108#define INCORE_BASE(p) ((caddr_t)((u_long)(&(p)->xs_h2xhdr) & 0xFFFFFFF0))
1090d1e0 109#define RVAL_OFF(unit, n) \
8011f5df 110 ((caddr_t)(&(ex_softc[unit].n)) - INCORE_BASE(&ex_softc[unit]))
1090d1e0 111#define LVAL_OFF(unit, n) \
8011f5df 112 ((caddr_t)(ex_softc[unit].n) - INCORE_BASE(&ex_softc[unit]))
1090d1e0
KM
113#define H2XHDR_OFFSET(unit) RVAL_OFF(unit, xs_h2xhdr)
114#define X2HHDR_OFFSET(unit) RVAL_OFF(unit, xs_x2hhdr)
115#define H2XENT_OFFSET(unit) LVAL_OFF(unit, xs_h2xent)
116#define X2HENT_OFFSET(unit) LVAL_OFF(unit, xs_x2hent)
117#define CM_OFFSET(unit) RVAL_OFF(unit, xs_cm)
118#define SA_OFFSET(unit) RVAL_OFF(unit, xs_xsa)
119#define INCORE_SIZE(unit) RVAL_OFF(unit, xs_end)
063e82dc
MK
120 int xs_end; /* place holder */
121} ex_softc[NEX];
122
123/*
124 * The following structure is a kludge to store a cvec value
125 * between the time exprobe is called, and exconfig.
126 */
127struct ex_cvecs {
128 struct exdevice *xc_csraddr;
129 int xc_cvec;
130}ex_cvecs[NEX];
131
132int ex_ncall = 0; /* counts calls to exprobe */
133
134exprobe(reg)
135 caddr_t reg;
136{
137 register int br, cvec; /* r11, r10 value-result */
138 register struct exdevice *addr = (struct exdevice *)reg;
139 register i;
140
141 /*
142 * We program the EXOS interrupt vector, like dmf device.
143 */
144 br = 0x15;
145 cvec = (uba_hd[numuba].uh_lastiv -= 4);
063e82dc 146 ex_cvecs[ex_ncall].xc_csraddr = addr;
3555b4b1 147 ex_cvecs[ex_ncall].xc_cvec = cvec;
063e82dc
MK
148 /*
149 * Reset EXOS and run self-test (guaranteed to
150 * complete within 2 seconds).
151 */
152 addr->xd_porta = EX_RESET;
3555b4b1 153 i = 2000;
063e82dc 154 while (((addr->xd_portb & EX_TESTOK) == 0) && --i)
3555b4b1 155 DELAY(1000);
063e82dc
MK
156 if ((addr->xd_portb & EX_TESTOK) == 0) {
157 printf("ex: self-test failed\n");
158 return 0;
159 }
7bdbbe60
MK
160#ifdef lint
161 br = br;
8011f5df 162 excdint(0);
7bdbbe60 163#endif
3555b4b1 164 ex_ncall++;
063e82dc
MK
165 return (sizeof(struct exdevice));
166}
167
168/*
169 * Interface exists: make available by filling in network interface
170 * record. System will initialize the interface when it is ready
171 * to accept packets. Board is temporarily configured and issues
172 * a NET_ADDRS command, only to get the Ethernet address.
173 */
174exattach(ui)
175 struct uba_device *ui;
176{
177 register struct ex_softc *xs = &ex_softc[ui->ui_unit];
178 register struct ifnet *ifp = &xs->xs_if;
179 register struct exdevice *addr = (struct exdevice *)ui->ui_addr;
063e82dc 180 register struct ex_msg *bp;
1090d1e0 181 int unit = ui->ui_unit;
063e82dc
MK
182 ifp->if_unit = ui->ui_unit;
183 ifp->if_name = "ex";
184 ifp->if_mtu = ETHERMTU;
185
186 /*
187 * Temporarily map queues in order to configure EXOS
188 */
1090d1e0
KM
189 xs->xs_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(xs),
190 INCORE_SIZE(unit), 0);
063e82dc
MK
191 exconfig(ui, 0); /* without interrupts */
192 if (xs->xs_cm.cm_cc) goto badconf;
193
194 bp = exgetcbuf(xs);
195 bp->mb_rqst = LLNET_ADDRS;
196 bp->mb_na.na_mask = READ_OBJ;
197 bp->mb_na.na_slot = PHYSSLOT;
198 bp->mb_status |= MH_EXOS;
199 addr->xd_portb = EX_NTRUPT;
200 bp = xs->xs_x2hnext;
201 while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
202 ;
87c6a528 203 printf("ex%d: HW %c.%c, NX %c.%c, hardware address %s\n",
3555b4b1
MK
204 ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
205 xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
206 ether_sprintf(bp->mb_na.na_addrs));
063e82dc
MK
207 bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
208 sizeof (xs->xs_addr));
209
063e82dc
MK
210 ifp->if_init = exinit;
211 ifp->if_output = exoutput;
212 ifp->if_ioctl = exioctl;
213 ifp->if_reset = exreset;
1a568327 214 ifp->if_flags = IFF_BROADCAST;
063e82dc
MK
215 xs->xs_ifuba.ifu_flags = UBA_CANTWAIT;
216 if_attach(ifp);
217badconf:
218 ubarelse(ui->ui_ubanum, &xs->xs_ubaddr);
219}
220
221/*
222 * Reset of interface after UNIBUS reset.
223 * If interface is on specified uba, reset its state.
224 */
225exreset(unit, uban)
226 int unit, uban;
227{
228 register struct uba_device *ui;
229
230 if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0 ||
231 ui->ui_ubanum != uban)
232 return;
233 printf(" ex%d", unit);
1a568327 234 ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
0f487295 235 ex_softc[unit].xs_flags &= ~EX_RUNNING;
063e82dc
MK
236 exinit(unit);
237}
238
239/*
240 * Initialization of interface; clear recorded pending
241 * operations, and reinitialize UNIBUS usage.
242 * Called at boot time (with interrupts disabled?),
243 * and at ifconfig time via exioctl, with interrupts disabled.
244 */
245exinit(unit)
246 int unit;
247{
248 register struct ex_softc *xs = &ex_softc[unit];
249 register struct uba_device *ui = exinfo[unit];
250 register struct exdevice *addr = (struct exdevice *)ui->ui_addr;
251 register struct ifnet *ifp = &xs->xs_if;
063e82dc
MK
252 register struct ex_msg *bp;
253 int s;
254
1a568327
MK
255 /* not yet, if address still unknown */
256 if (ifp->if_addrlist == (struct ifaddr *)0)
063e82dc 257 return;
0f487295 258 if (xs->xs_flags & EX_RUNNING)
063e82dc 259 return;
0f487295
MK
260
261 if ((ifp->if_flags & IFF_RUNNING) == 0) {
262 if (if_ubainit(&xs->xs_ifuba, ui->ui_ubanum,
263 sizeof (struct ether_header),
264 (int)btoc(EXMAXRBUF-sizeof(struct ether_header))) == 0) {
265 printf("ex%d: can't initialize\n", unit);
266 xs->xs_if.if_flags &= ~IFF_UP;
267 return;
268 }
269 xs->xs_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(xs),
1090d1e0 270 INCORE_SIZE(unit), 0);
063e82dc 271 }
063e82dc
MK
272 exconfig(ui, 4); /* with vectored interrupts*/
273 /*
274 * Put EXOS on the Ethernet, using NET_MODE command
275 */
276 bp = exgetcbuf(xs);
277 bp->mb_rqst = LLNET_MODE;
278 bp->mb_nm.nm_mask = WRITE_OBJ;
279 bp->mb_nm.nm_optn = 0;
280 bp->mb_nm.nm_mode = MODE_PERF;
281 bp->mb_status |= MH_EXOS;
282 addr->xd_portb = EX_NTRUPT;
283 bp = xs->xs_x2hnext;
284 while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
285 ;
286 bp->mb_length = MBDATALEN;
287 bp->mb_status |= MH_EXOS; /* free up buffer */
288 addr->xd_portb = EX_NTRUPT; /* tell EXOS about it */
289 xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
290
291 ifp->if_watchdog = exwatch;
292 ifp->if_timer = EXWATCHINTVL;
293 s = splimp(); /* are interrupts always disabled here, anyway? */
294 exhangrcv(unit); /* hang receive request */
1a568327 295 xs->xs_if.if_flags |= IFF_RUNNING;
0f487295 296 xs->xs_flags |= EX_RUNNING;
be85b93a 297 if (xs->xs_flags & EX_SETADDR)
8011f5df 298 ex_setaddr((u_char *)0, unit);
be85b93a 299 exstart(unit); /* start transmits */
063e82dc 300 splx(s);
063e82dc
MK
301}
302
303/*
304 * Reset, test, and configure EXOS. This routine assumes
305 * that message queues, etc. have already been mapped into
306 * the UBA. It is called by exinit, and should also be
307 * callable by exattach.
308 */
309exconfig(ui, itype)
310 struct uba_device *ui;
311 int itype;
312{
313 register int unit = ui->ui_unit;
314 register struct ex_softc *xs = &ex_softc[unit];
315 register struct exdevice *addr = (struct exdevice *) ui->ui_addr;
316 register struct confmsg *cm = &xs->xs_cm;
317 register struct ex_msg *bp;
318 int i;
319 u_long shiftreg;
320
321 xs->xs_flags = 0;
322 /*
323 * Reset EXOS, wait for self-test to complete
324 */
325 addr->xd_porta = EX_RESET;
326 while ((addr->xd_portb & EX_TESTOK) == 0)
327 ;
328 /*
329 * Set up configuration message.
330 */
331 cm->cm_1rsrv = 1;
332 cm->cm_cc = 0xFF;
333 cm->cm_opmode = 0; /* link-level controller mode */
334 cm->cm_dfo = 0x0101; /* enable host data order conversion */
335 cm->cm_dcn1 = 1;
336 cm->cm_2rsrv[0] =
337 cm->cm_2rsrv[1] = 0;
338 cm->cm_ham = 3; /* absolute address mode */
339 cm->cm_3rsrv = 0;
340 cm->cm_mapsiz = 0;
341 cm->cm_byteptrn[0] = 0x01; /* EXOS deduces data order of host */
342 cm->cm_byteptrn[1] = 0x03; /* by looking at this pattern */
343 cm->cm_byteptrn[2] = 0x07;
344 cm->cm_byteptrn[3] = 0x0F;
345 cm->cm_wordptrn[0] = 0x0103;
346 cm->cm_wordptrn[1] = 0x070F;
347 cm->cm_lwordptrn = 0x0103070F;
348 for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
349 cm->cm_mba = 0xFFFFFFFF;
350 cm->cm_nproc = 0xFF;
351 cm->cm_nmbox = 0xFF;
352 cm->cm_nmcast = 0xFF;
353 cm->cm_nhost = 1;
354 cm->cm_h2xba = P_UNIADDR(xs->xs_ubaddr);
1090d1e0 355 cm->cm_h2xhdr = H2XHDR_OFFSET(unit);
063e82dc
MK
356 cm->cm_h2xtyp = 0; /* should never wait for rqst buffer */
357 cm->cm_x2hba = cm->cm_h2xba;
1090d1e0 358 cm->cm_x2hhdr = X2HHDR_OFFSET(unit);
063e82dc
MK
359 cm->cm_x2htyp = itype; /* 0 for none, 4 for vectored */
360 for (i=0; (addr != ex_cvecs[i].xc_csraddr); i++)
361#ifdef DEBUG
362 if (i >= NEX)
363 panic("ex: matching csr address not found");
364#endif
365 ;
366 cm->cm_x2haddr = ex_cvecs[i].xc_cvec; /* stashed here by exprobe */
367 /*
368 * Set up message queues and headers.
369 * First the request queue.
370 */
371 for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) {
372 bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
373 bp->mb_rsrv = 0;
374 bp->mb_length = MBDATALEN;
375 bp->mb_status = MH_HOST;
376 bp->mb_next = bp+1;
377 }
378 xs->xs_h2xhdr =
379 xs->xs_h2xent[NH2X-1].mb_link =
1090d1e0 380 (u_short)H2XENT_OFFSET(unit);
063e82dc
MK
381 xs->xs_h2xnext =
382 xs->xs_h2xent[NH2X-1].mb_next =
383 xs->xs_h2xent;
384
385 /* Now the reply queue. */
386 for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
387 bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
388 bp->mb_rsrv = 0;
389 bp->mb_length = MBDATALEN;
390 bp->mb_status = MH_EXOS;
391 bp->mb_next = bp+1;
392 }
393 xs->xs_x2hhdr =
394 xs->xs_x2hent[NX2H-1].mb_link =
1090d1e0 395 (u_short)X2HENT_OFFSET(unit);
063e82dc
MK
396 xs->xs_x2hnext =
397 xs->xs_x2hent[NX2H-1].mb_next =
398 xs->xs_x2hent;
399
400 /*
401 * Write config msg address to EXOS and wait for
402 * configuration to complete (guaranteed response
403 * within 2 seconds).
404 */
405 shiftreg = (u_long)0x0000FFFF;
406 for (i = 0; i < 8; i++) {
407 if (i == 4)
1090d1e0 408 shiftreg = P_UNIADDR(xs->xs_ubaddr) + CM_OFFSET(unit);
063e82dc
MK
409 while (addr->xd_portb & EX_UNREADY)
410 ;
411 addr->xd_portb = (u_char)(shiftreg & 0xFF);
412 shiftreg >>= 8;
413 }
414 for (i = 1000000; (cm->cm_cc == 0xFF) && i; --i);
415 if (cm->cm_cc)
416 printf("ex%d: configuration failed; cc = %x\n",
417 unit, cm->cm_cc);
418}
419
420/*
421 * Start or re-start output on interface.
422 * Get another datagram to send off of the interface queue,
423 * and map it to the interface before starting the output.
424 * This routine is called by exinit(), exoutput(), and excdint().
425 * In all cases, interrupts by EXOS are disabled.
426 */
427exstart(unit)
428 int unit;
429{
430 struct uba_device *ui = exinfo[unit];
431 register struct ex_softc *xs = &ex_softc[unit];
432 register struct exdevice *addr = (struct exdevice *)ui->ui_addr;
433 register struct ex_msg *bp;
434 struct mbuf *m;
435 int len;
436
437#ifdef DEBUG
438 if (xs->xs_flags & EX_XPENDING)
439 panic("exstart(): xmit still pending");
440#endif
441 IF_DEQUEUE(&xs->xs_if.if_snd, m);
442 if (m == 0)
443 return;
444 len = if_wubaput(&xs->xs_ifuba, m);
445 if (len - sizeof(struct ether_header) < ETHERMIN)
446 len = ETHERMIN + sizeof(struct ether_header);
447 /*
448 * Place a transmit request.
449 */
450 bp = exgetcbuf(xs);
451 bp->mb_rqst = LLRTRANSMIT;
452 bp->mb_et.et_nblock = 1;
453 bp->mb_et.et_blks[0].bb_len = (u_short)len;
454 *(u_long *)bp->mb_et.et_blks[0].bb_addr =
455 UNIADDR(xs->xs_ifuba.ifu_w.ifrw_info);
456 xs->xs_flags |= EX_XPENDING;
457 bp->mb_status |= MH_EXOS;
458 addr->xd_portb = EX_NTRUPT;
459}
460
461/*
462 * Command done interrupt.
463 */
464excdint(unit)
465 int unit;
466{
467 register struct ex_softc *xs = &ex_softc[unit];
468 register struct ex_msg *bp = xs->xs_x2hnext;
469 struct uba_device *ui = exinfo[unit];
470 struct exdevice *addr = (struct exdevice *)ui->ui_addr;
471
472 while ((bp->mb_status & MH_OWNER) == MH_HOST) {
473 switch (bp->mb_rqst) {
474 case LLRECEIVE:
475 exrecv(unit, bp);
476 exhangrcv(unit);
477 break;
478 case LLRTRANSMIT:
479#ifdef DEBUG
480 if ((xs->xs_flags & EX_XPENDING) == 0)
481 panic("exxmit: no xmit pending");
482#endif
483 xs->xs_flags &= ~EX_XPENDING;
484 xs->xs_if.if_opackets++;
485 if (bp->mb_rply == LL_OK) {
486 ;
487 } else if (bp->mb_rply & LLXM_1RTRY) {
488 xs->xs_if.if_collisions++;
489 } else if (bp->mb_rply & LLXM_RTRYS) {
490 xs->xs_if.if_collisions += 2; /* guess */
491 } else if (bp->mb_rply & LLXM_ERROR) {
492 xs->xs_if.if_oerrors++;
493 printf("ex%d: transmit error=%b\n",
494 unit, bp->mb_rply, XMIT_BITS);
495 }
496 if (xs->xs_ifuba.ifu_xtofree) {
497 m_freem(xs->xs_ifuba.ifu_xtofree);
498 xs->xs_ifuba.ifu_xtofree = 0;
499 }
500 exstart(unit);
501 break;
502 case LLNET_STSTCS:
503 xs->xs_if.if_ierrors = xs->xs_xsa.sa_crc;
504 xs->xs_flags &= ~EX_STATPENDING;
505 break;
be85b93a
KS
506 case LLNET_ADDRS:
507 case LLNET_RECV:
508 break;
063e82dc
MK
509#ifdef DEBUG
510 default:
511 panic("ex%d: unknown reply");
512#endif
513 } /* end of switch */
514 bp->mb_length = MBDATALEN;
515 bp->mb_status |= MH_EXOS; /* free up buffer */
516 addr->xd_portb = EX_NTRUPT; /* tell EXOS about it */
517 bp = xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
518 }
519}
520
521/*
522 * Get a request buffer, fill in standard values, advance pointer.
523 */
524struct ex_msg *
525exgetcbuf(xs)
526 struct ex_softc *xs;
527{
528 register struct ex_msg *bp = xs->xs_h2xnext;
529
530#ifdef DEBUG
531 if ((bp->mb_status & MH_OWNER) == MH_EXOS)
532 panic("exgetcbuf(): EXOS owns message buffer");
533#endif
534 bp->mb_1rsrv = 0;
535 bp->mb_length = MBDATALEN;
536 xs->xs_h2xnext = xs->xs_h2xnext->mb_next;
537 return bp;
538}
539
540/*
541 * Process Ethernet receive completion:
542 * If input error just drop packet.
543 * Otherwise purge input buffered data path and examine
544 * packet to determine type. If can't determine length
545 * from type, then have to drop packet. Otherwise decapsulate
546 * packet based on type and pass to type-specific higher-level
547 * input routine.
548 */
549exrecv(unit, bp)
550 int unit;
551 register struct ex_msg *bp;
552{
553 register struct ex_softc *xs = &ex_softc[unit];
554 register struct ether_header *eh;
555 struct mbuf *m;
556 register int len, off, resid;
557 register struct ifqueue *inq;
558
559 xs->xs_if.if_ipackets++;
560 len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
561 if (bp->mb_rply != LL_OK) {
562 xs->xs_if.if_ierrors++;
563 printf("ex%d: receive error=%b\n",
564 unit, bp->mb_rply, RECV_BITS);
565 return;
566 }
567 eh = (struct ether_header *)(xs->xs_ifuba.ifu_r.ifrw_addr);
568
569 /*
1a568327 570 * Deal with trailer protocol: if type is trailer
063e82dc
MK
571 * get true type from first 16-bit word past data.
572 * Remember that type was trailer by setting off.
573 */
574 eh->ether_type = ntohs((u_short)eh->ether_type);
575#define exdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
1a568327
MK
576 if (eh->ether_type >= ETHERTYPE_TRAIL &&
577 eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
578 off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
063e82dc
MK
579 if (off >= ETHERMTU)
580 return; /* sanity */
581 eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
582 resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
583 if (off + resid > len)
584 return; /* sanity */
585 len = off + resid;
586 } else
587 off = 0;
588 if (len == 0)
589 return;
590
591 /*
592 * Pull packet off interface. Off is nonzero if packet
593 * has trailing header; if_rubaget will then force this header
594 * information to be at the front, but we still have to drop
595 * the type and length which are at the front of any trailer data.
596 */
7bdbbe60 597 m = if_rubaget(&xs->xs_ifuba, len, off, &xs->xs_if);
063e82dc
MK
598 if (m == 0)
599 return;
600 if (off) {
7bdbbe60
MK
601 struct ifnet *ifp;
602
603 ifp = *(mtod(m, struct ifnet **));
063e82dc
MK
604 m->m_off += 2 * sizeof (u_short);
605 m->m_len -= 2 * sizeof (u_short);
7bdbbe60 606 *(mtod(m, struct ifnet **)) = ifp;
063e82dc
MK
607 }
608 switch (eh->ether_type) {
609
610#ifdef INET
1a568327 611 case ETHERTYPE_IP:
063e82dc
MK
612 schednetisr(NETISR_IP); /* is this necessary */
613 inq = &ipintrq;
614 break;
615
1a568327 616 case ETHERTYPE_ARP:
063e82dc
MK
617 arpinput(&xs->xs_ac, m);
618 return;
f2803948
KS
619#endif
620#ifdef NS
621 case ETHERTYPE_NS:
622 schednetisr(NETISR_NS);
623 inq = &nsintrq;
624 break;
625
063e82dc
MK
626#endif
627 default:
628 m_freem(m);
629 return;
630 }
631
632 if (IF_QFULL(inq)) {
633 IF_DROP(inq);
634 m_freem(m);
635 return;
636 }
637 IF_ENQUEUE(inq, m);
638}
639
640/*
641 * Send receive request to EXOS.
642 * This routine is called by exinit and excdint,
643 * with interrupts disabled in both cases.
644 */
645exhangrcv(unit)
646 int unit;
647{
648 register struct ex_softc *xs = &ex_softc[unit];
649 register struct ex_msg *bp = exgetcbuf(xs);
650 struct exdevice *addr = (struct exdevice *)exinfo[unit]->ui_addr;
651
652 bp->mb_rqst = LLRECEIVE;
653 bp->mb_er.er_nblock = 1;
654 bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
655 *(u_long *)bp->mb_er.er_blks[0].bb_addr =
656 UNIADDR(xs->xs_ifuba.ifu_r.ifrw_info);
657 bp->mb_status |= MH_EXOS;
658 addr->xd_portb = EX_NTRUPT;
659}
660
661/*
662 * Ethernet output routine.
663 * Encapsulate a packet of type family for the local net.
664 * Use trailer local net encapsulation if enough data in first
665 * packet leaves a multiple of 512 bytes of data in remainder.
666 */
667exoutput(ifp, m0, dst)
668 register struct ifnet *ifp;
669 register struct mbuf *m0;
670 struct sockaddr *dst;
671{
672 int type, s, error;
673 u_char edst[6];
674 struct in_addr idst;
675 register struct ex_softc *xs = &ex_softc[ifp->if_unit];
676 register struct mbuf *m = m0;
677 register struct ether_header *eh;
678 register int off;
87c6a528 679 int usetrailers;
063e82dc 680
0f487295
MK
681 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
682 error = ENETDOWN;
683 goto bad;
684 }
063e82dc
MK
685 switch (dst->sa_family) {
686
687#ifdef INET
688 case AF_INET:
689 idst = ((struct sockaddr_in *)dst)->sin_addr;
87c6a528 690 if (!arpresolve(&xs->xs_ac, m, &idst, edst, &usetrailers))
063e82dc
MK
691 return (0); /* if not yet resolved */
692 off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
87c6a528 693 if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
063e82dc 694 m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
1a568327 695 type = ETHERTYPE_TRAIL + (off>>9);
063e82dc
MK
696 m->m_off -= 2 * sizeof (u_short);
697 m->m_len += 2 * sizeof (u_short);
1a568327 698 *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
063e82dc
MK
699 *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
700 goto gottrailertype;
701 }
1a568327 702 type = ETHERTYPE_IP;
063e82dc
MK
703 off = 0;
704 goto gottype;
705#endif
f2803948
KS
706#ifdef NS
707 case AF_NS:
708 type = ETHERTYPE_NS;
709 bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
710 (caddr_t)edst, sizeof (edst));
711 off = 0;
712 goto gottype;
713#endif
063e82dc
MK
714
715 case AF_UNSPEC:
716 eh = (struct ether_header *)dst->sa_data;
717 bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
718 type = eh->ether_type;
719 goto gottype;
720
721 default:
722 printf("ex%d: can't handle af%d\n", ifp->if_unit,
723 dst->sa_family);
724 error = EAFNOSUPPORT;
725 goto bad;
726 }
727
728gottrailertype:
729 /*
730 * Packet to be sent as trailer: move first packet
731 * (control information) to end of chain.
732 */
733 while (m->m_next)
734 m = m->m_next;
735 m->m_next = m0;
736 m = m0->m_next;
737 m0->m_next = 0;
738 m0 = m;
739
740gottype:
741 /*
742 * Add local net header. If no space in first mbuf,
743 * allocate another.
744 */
745 if (m->m_off > MMAXOFF ||
746 MMINOFF + sizeof (struct ether_header) > m->m_off) {
747 m = m_get(M_DONTWAIT, MT_HEADER);
748 if (m == 0) {
749 error = ENOBUFS;
750 goto bad;
751 }
752 m->m_next = m0;
753 m->m_off = MMINOFF;
754 m->m_len = sizeof (struct ether_header);
755 } else {
756 m->m_off -= sizeof (struct ether_header);
757 m->m_len += sizeof (struct ether_header);
758 }
759 eh = mtod(m, struct ether_header *);
760 eh->ether_type = htons((u_short)type);
761 bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
762 bcopy((caddr_t)xs->xs_addr, (caddr_t)eh->ether_shost, 6);
763
764 /*
765 * Queue message on interface, and start output if interface
766 * not yet active.
767 */
768 s = splimp();
769 if (IF_QFULL(&ifp->if_snd)) {
770 IF_DROP(&ifp->if_snd);
771 splx(s);
772 m_freem(m);
773 return (ENOBUFS);
774 }
775 IF_ENQUEUE(&ifp->if_snd, m);
776 /*
777 * If transmit request not already pending, then
778 * kick the back end.
779 */
780 if ((xs->xs_flags & EX_XPENDING) == 0) {
781 exstart(ifp->if_unit);
782 }
783#ifdef DEBUG
784 else {
785 xs->xs_wait++;
786 }
787#endif
788 splx(s);
789 return (0);
790
791bad:
792 m_freem(m0);
793 return (error);
794}
795
796/*
797 * Watchdog routine - place stats request to EXOS
798 * (This could be dispensed with, if you don't care
799 * about the if_ierrors count, or are willing to receive
800 * bad packets in order to derive it.)
801 */
802exwatch(unit)
803 int unit;
804{
805 struct uba_device *ui = exinfo[unit];
806 struct exdevice *addr = (struct exdevice *)ui->ui_addr;
807 register struct ex_softc *xs = &ex_softc[unit];
808 register struct ex_msg *bp;
809 int s = splimp();
810
811 if (xs->xs_flags & EX_STATPENDING) goto exspnd;
812 bp = exgetcbuf(xs);
813 xs->xs_flags |= EX_STATPENDING;
814 bp->mb_rqst = LLNET_STSTCS;
815 bp->mb_ns.ns_mask = READ_OBJ;
816 bp->mb_ns.ns_rsrv = 0;
817 bp->mb_ns.ns_nobj = 8; /* read all 8 stats objects */
818 bp->mb_ns.ns_xobj = 0; /* starting with the 1st one */
1090d1e0 819 bp->mb_ns.ns_bufp = P_UNIADDR(xs->xs_ubaddr) + SA_OFFSET(unit);
063e82dc
MK
820 bp->mb_status |= MH_EXOS;
821 addr->xd_portb = EX_NTRUPT;
822exspnd:
823 splx(s);
824 xs->xs_if.if_timer = EXWATCHINTVL;
825}
826
827/*
828 * Process an ioctl request.
829 */
830exioctl(ifp, cmd, data)
831 register struct ifnet *ifp;
832 int cmd;
833 caddr_t data;
834{
1a568327 835 register struct ifaddr *ifa = (struct ifaddr *)data;
0f487295 836 register struct ex_softc *xs = &ex_softc[ifp->if_unit];
063e82dc
MK
837 int s = splimp(), error = 0;
838
839 switch (cmd) {
840
841 case SIOCSIFADDR:
1a568327 842 ifp->if_flags |= IFF_UP;
7bdbbe60 843 exinit(ifp->if_unit);
1a568327
MK
844
845 switch (ifa->ifa_addr.sa_family) {
f2803948 846#ifdef INET
1a568327
MK
847 case AF_INET:
848 ((struct arpcom *)ifp)->ac_ipaddr =
849 IA_SIN(ifa)->sin_addr;
850 arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
851 break;
f2803948
KS
852#endif
853#ifdef NS
854 case AF_NS:
be85b93a
KS
855 {
856 register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
857
858 if (ns_nullhost(*ina))
859 ina->x_host = *(union ns_host *)(xs->xs_addr);
860 else
861 ex_setaddr(ina->x_host.c_host,ifp->if_unit);
f2803948 862 break;
be85b93a 863 }
f2803948 864#endif
1a568327 865 }
063e82dc
MK
866 break;
867
0f487295
MK
868 case SIOCSIFFLAGS:
869 if ((ifp->if_flags & IFF_UP) == 0 &&
870 xs->xs_flags & EX_RUNNING) {
871 ((struct exdevice *)
872 (exinfo[ifp->if_unit]->ui_addr))->xd_porta = EX_RESET;
873 xs->xs_flags &= ~EX_RUNNING;
874 } else if (ifp->if_flags & IFF_UP &&
875 (xs->xs_flags & EX_RUNNING) == 0)
876 exinit(ifp->if_unit);
877 break;
878
063e82dc
MK
879 default:
880 error = EINVAL;
881 }
882 splx(s);
883 return (error);
884}
be85b93a
KS
885
886/*
887 * set ethernet address for unit
888 */
889ex_setaddr(physaddr, unit)
890 u_char *physaddr;
891 int unit;
892{
893 register struct ex_softc *xs = &ex_softc[unit];
894 struct uba_device *ui = exinfo[unit];
895 register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
be85b93a
KS
896 register struct ex_msg *bp;
897
898 if (physaddr) {
899 xs->xs_flags |= EX_SETADDR;
900 bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
901 }
902 if (! (xs->xs_flags & EX_RUNNING))
903 return;
904 bp = exgetcbuf(xs);
905 bp->mb_rqst = LLNET_ADDRS;
906 bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
907 bp->mb_na.na_slot = PHYSSLOT;
8011f5df 908 bcopy((caddr_t)xs->xs_addr, (caddr_t)bp->mb_na.na_addrs, 6);
be85b93a
KS
909 bp->mb_status |= MH_EXOS;
910 addr->xd_portb = EX_NTRUPT;
911 bp = xs->xs_x2hnext;
912 while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
913 ;
87c6a528
MK
914#ifdef DEBUG
915 printf("ex%d: reset addr %s\n", ui->ui_unit,
916 ether_sprintf(bp->mb_na.na_addrs));
917#endif
be85b93a
KS
918 /*
919 * Now, re-enable reception on phys slot.
920 */
921 bp = exgetcbuf(xs);
922 bp->mb_rqst = LLNET_RECV;
923 bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
924 bp->mb_nr.nr_slot = PHYSSLOT;
925 bp->mb_status |= MH_EXOS;
926 addr->xd_portb = EX_NTRUPT;
927 bp = xs->xs_x2hnext;
928 while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
929 ;
930}
9a0b0c74 931#endif