first working version of BSD/tahoe ex driver; checkpoint other
[unix-history] / usr / src / sys / tahoe / if / if_ex.c
CommitLineData
19df187f
KS
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Excelan Inc.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
385e8f24 20 * @(#)if_ex.c 7.2 (Berkeley) %G%
19df187f
KS
21 */
22
23#include "ex.h"
24
25#if NEX > 0
26
27/*
28 * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers
29 */
30#include "param.h"
31#include "systm.h"
32#include "mbuf.h"
33#include "buf.h"
34#include "protosw.h"
35#include "socket.h"
36#include "vmmac.h"
37#include "ioctl.h"
38#include "errno.h"
39#include "vmparam.h"
40#include "syslog.h"
41#include "uio.h"
42
43#include "../net/if.h"
44#include "../net/netisr.h"
45#include "../net/route.h"
46
47#ifdef INET
48#include "../netinet/in.h"
49#include "../netinet/in_systm.h"
50#include "../netinet/in_var.h"
51#include "../netinet/ip.h"
52#include "../netinet/if_ether.h"
53#endif
54
55#ifdef NS
56#include "../netns/ns.h"
57#include "../netns/ns_if.h"
58#endif
59
385e8f24
KS
60#ifdef ISO
61#include "../netiso/iso.h"
62#include "../netiso/iso_var.h"
63#include "../netiso/iso_snpac.h"
64extern struct snpa_cache all_es, all_is;
65#endif
66
19df187f
KS
67#include "../tahoe/cpu.h"
68#include "../tahoe/pte.h"
69#include "../tahoe/mtpr.h"
70
71#include "../tahoevba/vbavar.h"
72#include "if_exreg.h"
73#include "if_vba.h"
74
75
76#define NH2X 32 /* Host to eXcelan request buffers */
77
78#define NX2H 16 /* eXcelan to Host reply buffers */
79#define NREC 16 /* Number of RECeive buffers */
80#define NTRB 4 /* Number of TRansmit Buffers */
81#define NVBI (NREC + NTRB)
82
83#define EXWATCHINTVL 10 /* call exwatch every x secs */
84
85int exprobe(), exslave(), exattach(), exintr(), exstart();
86struct vba_device *exinfo[NEX];
87
88long exstd[] = { 0 };
89
90
91struct vba_driver exdriver =
92 { exprobe, 0, exattach, exstart, exstd, "ex", exinfo };
93int exinit(),ether_output(),exioctl(),exreset(),exwatch();
94struct ex_msg *exgetcbuf();
95int ex_ncall = 0; /* counts calls to exprobe */
96u_long busoff;
97
98/*
99 * Ethernet software status per interface.
100 *
101 * Each interface is referenced by a network interface structure, xs_if, which
102 * the routing code uses to locate the interface. This structure contains the
103 * output queue for the interface, its address, ... NOTE: To configure multiple
104 * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr).
105 */
106struct ex_softc {
107 struct arpcom xs_ac; /* Ethernet common part */
108#define xs_if xs_ac.ac_if /* network-visible interface */
109#define xs_addr xs_ac.ac_enaddr /* hardware Ethernet address */
110 int xs_flags; /* private flags */
111#define EX_XPENDING 1 /* xmit rqst pending on EXOS */
112#define EX_STATPENDING (1<<1) /* stats rqst pending on EXOS */
113#define EX_RUNNING (1<<2) /* board is running */
114#define EX_SETADDR (1<<3) /* physaddr has been changed */
115 int xs_cvec; /* probe stores cvec here */
116 short xs_enetunit; /* unit number for enet filtering */
117 short xs_enetinit; /* enet inetrface is initialized */
118 struct ex_msg *xs_h2xnext; /* host pointer to request queue */
119 struct ex_msg *xs_x2hnext; /* host pointer to reply queue */
120 u_long xs_qbaddr; /* map info for structs below */
385e8f24 121 struct ex_shm {
19df187f 122 /* the following structures are always mapped in */
385e8f24
KS
123 u_short sm_h2xhdr; /* EXOS's request queue header */
124 u_short sm_x2hhdr; /* EXOS's reply queue header */
125 struct ex_msg sm_h2xent[NH2X];/* request msg buffers */
126 struct ex_msg sm_x2hent[NX2H];/* reply msg buffers */
127 struct ex_conf sm_cm; /* configuration message */
128 struct ex_stat sm_xsa; /* EXOS writes stats here */
19df187f 129 /* end mapped area */
385e8f24
KS
130 } *xs_shm; /* host pointer to shared area */
131#define xs_h2xhdr xs_shm->sm_h2xhdr
132#define xs_x2hhdr xs_shm->sm_x2hhdr
133#define xs_h2xent xs_shm->sm_h2xent
134#define xs_x2hent xs_shm->sm_x2hent
135#define xs_cm xs_shm->sm_cm
136#define xs_xsa xs_shm->sm_xsa
137#define BUSADDR(x) (0x3D000000 | (((u_long)kvtophys(x))&0xFFFFFF))
138#define P_BUSADDR(x) (0x3D000000 | (((u_long)kvtophys(x))&0xFFFFF0))
139#define INCORE_BASE(p) (((u_long)(p)->xs_shm) & 0xFFFFFFF0)
140/* we will arrange that the shared memory begins on a 16 byte boundary */
141#define RVAL_OFF(n) (((char *)&(((struct ex_shm *)0)->n))-(char *)0)
142#define LVAL_OFF(n) (((char *)(((struct ex_shm *)0)->n))-(char *)0)
143#define H2XHDR_OFFSET RVAL_OFF(sm_h2xhdr)
144#define X2HHDR_OFFSET RVAL_OFF(sm_x2hhdr)
145#define H2XENT_OFFSET LVAL_OFF(sm_h2xent)
146#define X2HENT_OFFSET LVAL_OFF(sm_x2hent)
147#define CM_OFFSET RVAL_OFF(sm_cm)
148#define SA_OFFSET RVAL_OFF(sm_xsa)
19df187f
KS
149 struct ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */
150 struct ifvba *xs_pkblist; /* free list of above */
385e8f24
KS
151#define GetPkBuf(b, v) ((v = (b)->mb_pkb = xs->xs_pkblist),\
152 (xs->xs_pkblist = (struct ifvba *)(v)->iff_mbuf))
153#define FreePkBuf(v) (((v)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\
154 (xs->xs_pkblist = v))
19df187f
KS
155 char xs_nrec; /* number of pending receive buffers */
156 char xs_ntrb; /* number of pending transmit buffers */
19df187f
KS
157} ex_softc[NEX];
158
159int ex_padcheck = sizeof (struct ex_softc);
160
161exprobe(reg, vi)
162 caddr_t reg;
163 struct vba_device *vi;
164{
165 register br, cvec; /* r12, r11 value-result */
166 register struct exdevice *exaddr = (struct exdevice *)reg;
167 int i;
168
385e8f24 169 if (badaddr((caddr_t)exaddr, 2))
19df187f
KS
170 return 0;
171 /*
172 * Reset EXOS and run self-test (should complete within 2 seconds).
173 */
174 movow(&exaddr->ex_porta, EX_RESET);
175 for (i = 1000000; i; i--) {
176 uncache(&(exaddr->ex_portb));
177 if (exaddr->ex_portb & EX_TESTOK)
178 break;
179 }
180 if ((exaddr->ex_portb & EX_TESTOK) == 0)
181 return 0;
182 br = 0x15;
183 cvec = --vi->ui_hd->vh_lastiv;
184 ex_softc[vi->ui_unit].xs_cvec = cvec;
185 ex_ncall++;
186 return (sizeof(struct exdevice));
187}
188
189/*
190 * Interface exists: make available by filling in network interface record.
191 * System will initialize the interface when it is ready to accept packets.
192 * A NET_ADDRS command is done to get the ethernet address.
193 */
194exattach(ui)
195 register struct vba_device *ui;
196{
197 register struct ex_softc *xs = &ex_softc[ui->ui_unit];
198 register struct ifnet *ifp = &xs->xs_if;
199 register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
200 register struct ex_msg *bp;
201
202 ifp->if_unit = ui->ui_unit;
203 ifp->if_name = "ex";
204 ifp->if_mtu = ETHERMTU;
205 ifp->if_init = exinit;
206 ifp->if_ioctl = exioctl;
207 ifp->if_output = ether_output;
208 ifp->if_reset = exreset;
209 ifp->if_start = exstart;
385e8f24 210 ifp->if_flags = IFF_BROADCAST;
19df187f 211
385e8f24
KS
212 /*
213 * Note: extra memory gets returned by if_vbareserve()
214 * first, so, being page alligned, it is also 16-byte alligned.
215 */
216 if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF,
217 (caddr_t *)&xs->xs_shm, sizeof(*xs->xs_shm)) == 0)
19df187f
KS
218 return;
219 /*
220 * Temporarily map queues in order to configure EXOS
221 */
222 xs->xs_qbaddr = INCORE_BASE(xs);
223 exconfig(ui, 0); /* without interrupts */
224 if (xs->xs_cm.cm_cc)
225 return; /* bad conf */
226 /*
227 * Get Ethernet address.
228 */
229 if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0)
230 panic("exattach");
231 bp->mb_na.na_mask = READ_OBJ;
232 bp->mb_na.na_slot = PHYSSLOT;
233 bp->mb_status |= MH_EXOS;
234 movow(&exaddr->ex_portb, EX_NTRUPT);
235 bp = xs->xs_x2hnext;
385e8f24 236 while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
19df187f
KS
237 printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n",
238 ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
239 xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
240 ether_sprintf(bp->mb_na.na_addrs));
241 bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
242 sizeof(xs->xs_addr));
243 if_attach(ifp);
244}
245
246/*
247 * Reset of interface after BUS reset.
248 * If interface is on specified vba, reset its state.
249 */
250exreset(unit)
251int unit;
252{
253 register struct vba_device *ui;
254
255 if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0)
256 return;
257 printf(" ex%d", unit);
258 ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
259 ex_softc[unit].xs_flags &= ~EX_RUNNING;
260
261 exinit(unit);
262}
263
264/*
265 * Initialization of interface; clear recorded pending operations, and
266 * reinitialize BUS usage. Called at boot time, and at ifconfig time via
267 * exioctl, with interrupts disabled.
268 */
269exinit(unit)
270int unit;
271{
272 register struct ex_softc *xs = &ex_softc[unit];
273 register struct vba_device *ui = exinfo[unit];
274 register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
275 register struct ifnet *ifp = &xs->xs_if;
276 register struct sockaddr_in *sin;
277 register struct ex_msg *bp;
278 int s;
279
280 /* not yet, if address still unknown */
281 if (ifp->if_addrlist == (struct ifaddr *)0)
282 return;
283 if (xs->xs_flags & EX_RUNNING)
284 return;
285
286 xs->xs_qbaddr = INCORE_BASE(xs);
287 exconfig(ui, 4); /* with vectored interrupts*/
288
289 /*
290 * Put EXOS on the Ethernet, using NET_MODE command
291 */
292 if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0)
293 panic("exinit");
294 bp->mb_nm.nm_mask = WRITE_OBJ;
295 bp->mb_nm.nm_optn = 0;
296 bp->mb_nm.nm_mode = MODE_PERF;
297 bp->mb_status |= MH_EXOS;
298 movow(&exaddr->ex_portb, EX_NTRUPT);
299 bp = xs->xs_x2hnext;
385e8f24
KS
300 while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
301 ;
19df187f
KS
302 bp->mb_length = MBDATALEN;
303 bp->mb_status |= MH_EXOS; /* free up buffer */
304 movow(&exaddr->ex_portb, EX_NTRUPT);
305 xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
306
307 ifp->if_watchdog = exwatch;
308 ifp->if_timer = EXWATCHINTVL;
309 s = splimp(); /* are interrupts disabled here, anyway? */
310 exhangrcv(unit);
311 xs->xs_if.if_flags |= IFF_RUNNING;
312 xs->xs_flags |= EX_RUNNING;
313 if (xs->xs_flags & EX_SETADDR)
314 ex_setaddr((u_char *)0, unit);
385e8f24
KS
315#ifdef ISO
316 ex_setmulti(all_es.sc_snpa, unit, 1);
317 ex_setmulti(all_is.sc_snpa, unit, 2);
318#endif
19df187f
KS
319 exstart(&ex_softc[unit].xs_if); /* start transmits */
320 splx(s); /* are interrupts disabled here, anyway? */
321}
322
323/*
324 * Reset, test, and configure EXOS. It is called by exinit, and exattach.
325 * Returns 0 if successful, 1 if self-test failed.
326 */
327exconfig(ui, itype)
328struct vba_device *ui;
329int itype;
330{
331 register int unit = ui->ui_unit;
332 register struct ex_softc *xs = &ex_softc[unit];
333 register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr;
385e8f24 334 register struct ex_conf *cm = &xs->xs_cm;
19df187f
KS
335 register struct ex_msg *bp;
336 register struct ifvba *pkb;
337 int i;
338 u_long shiftreg;
339 static u_char cmaddr[8] = {0xFF, 0xFF, 0, 0};
340
341 xs->xs_flags = 0;
342 /*
343 * Reset EXOS, wait for self-test to complete
344 */
345 movow(&exaddr->ex_porta, EX_RESET);
346 do {
347 uncache(&exaddr->ex_portb);
348 } while ((exaddr->ex_portb & EX_TESTOK) == 0) ;
349 /*
350 * Set up configuration message.
351 */
352 cm->cm_1rsrv = 1;
353 cm->cm_cc = 0xFF;
354 cm->cm_opmode = 0; /* link-level controller mode */
355 cm->cm_dfo = 0x0101; /* enable host data order conversion */
356 cm->cm_dcn1 = 1;
357 cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0;
358 cm->cm_ham = 3; /* absolute address mode */
359 cm->cm_3rsrv = 0;
360 cm->cm_mapsiz = 0;
361 cm->cm_byteptrn[0] = 0x01; /* EXOS deduces data order of host */
362 cm->cm_byteptrn[1] = 0x03; /* by looking at this pattern */
363 cm->cm_byteptrn[2] = 0x07;
364 cm->cm_byteptrn[3] = 0x0F;
365 cm->cm_wordptrn[0] = 0x0103;
366 cm->cm_wordptrn[1] = 0x070F;
367 cm->cm_lwordptrn = 0x0103070F;
368 for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
369 cm->cm_mba = 0xFFFFFFFF;
370 cm->cm_nproc = 0xFF;
371 cm->cm_nmbox = 0xFF;
372 cm->cm_nmcast = 0xFF;
373 cm->cm_nhost = 1;
374 cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr);
375 cm->cm_h2xhdr = H2XHDR_OFFSET;
376 cm->cm_h2xtyp = 0; /* should never wait for rqst buffer */
377 cm->cm_x2hba = cm->cm_h2xba;
378 cm->cm_x2hhdr = X2HHDR_OFFSET;
379 cm->cm_x2htyp = itype; /* 0 for none, 4 for vectored */
380 cm->cm_x2haddr = xs->xs_cvec; /* ivec allocated in exprobe */
381 /*
382 * Set up message queues and headers.
383 * First the request queue
384 */
385 for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) {
386 bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
387 bp->mb_rsrv = 0;
388 bp->mb_length = MBDATALEN;
389 bp->mb_status = MH_HOST;
390 bp->mb_next = bp+1;
391 }
392 xs->xs_h2xhdr = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET;
393 xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent;
394
395 /* Now the reply queue. */
396 for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
397 bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
398 bp->mb_rsrv = 0;
399 bp->mb_length = MBDATALEN;
400 bp->mb_status = MH_EXOS;
401 bp->mb_next = bp+1;
402 }
403 xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET;
404 xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent;
405 xs->xs_nrec = 0;
406 xs->xs_ntrb = 0;
407 xs->xs_pkblist = xs->xs_vbinfo + NVBI - 1;
385e8f24 408 for (pkb = xs->xs_pkblist; pkb > xs->xs_vbinfo; pkb--)
19df187f
KS
409 pkb->iff_mbuf = (struct mbuf *)(pkb - 1);
410 xs->xs_vbinfo[0].iff_mbuf = 0;
411
412 /*
413 * Write config msg address to EXOS and wait for configuration to
414 * complete (guaranteed response within 2 seconds).
415 */
416 shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET;
417 for (i = 4; i < 8; i++) {
418 cmaddr[i] = (u_char)(shiftreg & 0xFF);
419 shiftreg >>= 8;
420 }
421 for (i = 0; i < 8; i++) {
422 do {
423 uncache(&exaddr->ex_portb);
424 } while (exaddr->ex_portb & EX_UNREADY) ;
425 DELAY(500);
426 movow(&exaddr->ex_portb, cmaddr[i]);
427 }
428 for (i = 500000; i; --i) {
429 DELAY(10);
430 uncache(&cm->cm_cc);
431 if (cm->cm_cc != 0xFF)
432 break;
433 }
434 if (cm->cm_cc)
435 printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc);
436}
437
438/*
439 * Start or re-start output on interface. Get another datagram to send off of
440 * the interface queue, and map it to the interface before starting the output.
441 * This routine is called by exinit(), exoutput(), and excdint(). In all cases,
442 * interrupts by EXOS are disabled.
443 */
444exstart(ifp)
445struct ifnet *ifp;
446{
447 int unit = ifp->if_unit;
448 struct vba_device *ui = exinfo[unit];
449 register struct ex_softc *xs = &ex_softc[unit];
450 struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
451 register struct ex_msg *bp;
452 register struct mbuf *m;
453 int len;
454 register struct ifvba *pkb;
385e8f24
KS
455 struct mbuf *m0 = 0;
456 register int nb = 0, tlen = 0;
19df187f
KS
457 union l_util {
458 u_long l;
459 struct i86_long i;
460 } l_util;
461
462 if (xs->xs_ntrb >= NTRB)
463 return;
464 if (xs->xs_pkblist == 0) {
465 printf("ex%d: vbinfo exhausted, would panic", unit);
466 return;
467 }
468 IF_DEQUEUE(&xs->xs_if.if_snd, m);
469 if (m == 0)
470 return;
471 /*
472 * Get a transmit request.
473 */
474 if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) {
475 m_freem(m);
476 printf("exstart: no command buffers\n");
477 return;
478 }
479 xs->xs_ntrb++;
385e8f24 480 GetPkBuf(bp, pkb);
19df187f
KS
481 pkb->iff_mbuf = m; /* save mbuf pointer to free when done */
482 /*
483 * point directly to the first group of mbufs to be transmitted. The
484 * hardware can only support NFRAGMENTS descriptors.
485 */
486 while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) {
385e8f24 487 l_util.l = BUSADDR(mtod(m, caddr_t));
19df187f
KS
488 bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len;
489 bp->mb_et.et_blks[nb].bb_addr = l_util.i;
385e8f24
KS
490 if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
491 /* Here, the phys memory for the mbuf is out
492 of range for the vmebus to talk to it */
493 if (m == pkb->iff_mbuf)
494 pkb->iff_mbuf = 0;
495 break;
496 }
19df187f
KS
497 tlen += m->m_len;
498 m0 = m;
499 m = m->m_next;
500 nb++;
501 }
502
503 /* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */
504 if (m0)
505 m0->m_next = 0;
506
507 /*
508 * if not all of the descriptors would fit then merge remaining data
509 * into the transmit buffer, and point to it. Note: the mbufs are freed
510 * during the merge, they do not have to be freed when we get the
511 * transmit interrupt.
512 */
513 if (m) {
385e8f24
KS
514 if (m == pkb->iff_mbuf) {
515 printf("ex%d: exstart insanity\n", unit);
516 pkb->iff_mbuf = 0;
517 }
518 len = if_vbaput(pkb->iff_buffer, m, 0);
19df187f
KS
519 l_util.l = BUSADDR(pkb->iff_buffer);
520 bp->mb_et.et_blks[nb].bb_len = (u_short)len;
521 bp->mb_et.et_blks[nb].bb_addr = l_util.i;
522 tlen += len;
523 nb++;
524 }
525
526 /*
385e8f24
KS
527 * If the total length of the packet is too small,
528 * pad the last fragment. (May run into very obscure problems)
19df187f 529 */
385e8f24 530 if (tlen < sizeof(struct ether_header) + ETHERMIN) {
19df187f
KS
531 len = (ETHERMIN + sizeof(struct ether_header)) - tlen;
532 bp->mb_et.et_blks[nb-1].bb_len += (u_short)len;
533 tlen += len;
385e8f24
KS
534#ifdef notdef
535 if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
536 must copy last frag into private buffer
537 }
538#endif
19df187f
KS
539 }
540
541 /* set number of fragments in descriptor */
542 bp->mb_et.et_nblock = nb;
543 bp->mb_status |= MH_EXOS;
544 movow(&exaddr->ex_portb, EX_NTRUPT);
545}
546
547/*
548 * interrupt service routine.
549 */
550exintr(unit)
551 int unit;
552{
553 register struct ex_softc *xs = &ex_softc[unit];
554 register struct ex_msg *bp = xs->xs_x2hnext;
555 struct vba_device *ui = exinfo[unit];
556 struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
385e8f24 557 struct ex_msg *next_bp;
19df187f 558
19df187f
KS
559 while ((bp->mb_status & MH_OWNER) == MH_HOST) {
560 switch (bp->mb_rqst) {
561 case LLRECEIVE:
385e8f24
KS
562 if (--xs->xs_nrec < 0) {
563 printf("ex%d: internal receive check\n", unit);
19df187f 564 xs->xs_nrec = 0;
385e8f24 565 }
19df187f
KS
566 exrecv(unit, bp);
567 FreePkBuf(bp->mb_pkb);
568 bp->mb_pkb = (struct ifvba *)0;
569 exhangrcv(unit);
570 break;
571
572 case LLTRANSMIT:
573 case LLRTRANSMIT:
385e8f24
KS
574 if (--xs->xs_ntrb < 0) {
575 printf("ex%d: internal transmit check\n", unit);
19df187f 576 xs->xs_ntrb = 0;
385e8f24 577 }
19df187f 578 xs->xs_if.if_opackets++;
385e8f24 579 if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
19df187f
KS
580 ;
581 else if (bp->mb_rply & LLXM_1RTRY)
582 xs->xs_if.if_collisions++;
583 else if (bp->mb_rply & LLXM_RTRYS)
584 xs->xs_if.if_collisions += 2; /* guess */
585 else if (bp->mb_rply & LLXM_ERROR)
586 if (xs->xs_if.if_oerrors++ % 100 == 0)
587 printf("ex%d: 100 transmit errors=%b\n",
588 unit, bp->mb_rply, XMIT_BITS);
589 if (bp->mb_pkb->iff_mbuf) {
590 m_freem(bp->mb_pkb->iff_mbuf);
591 bp->mb_pkb->iff_mbuf = (struct mbuf *)0;
592 }
593 FreePkBuf(bp->mb_pkb);
594 bp->mb_pkb = (struct ifvba *)0;
385e8f24 595 exstart(&xs->xs_if);
19df187f
KS
596 exhangrcv(unit);
597 break;
598
599 case LLNET_STSTCS:
600 xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc;
601 xs->xs_flags &= ~EX_STATPENDING;
385e8f24
KS
602 case LLNET_ADDRS:
603 case LLNET_RECV:
604 if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
605 ;
606 else
607 printf("ex%d: %s, request 0x%x, reply 0x%x\n",
608 unit, "unsucessful stat or address change",
609 bp->mb_rqst, bp->mb_rply);
19df187f
KS
610 break;
611
612 default:
613 printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst);
614 }
615 bp->mb_length = MBDATALEN;
385e8f24 616 next_bp = bp->mb_next;
19df187f 617 bp->mb_status |= MH_EXOS; /* free up buffer */
385e8f24 618 bp = next_bp; /* paranoia about race */
19df187f 619 movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */
19df187f
KS
620 }
621 xs->xs_x2hnext = bp;
622}
623
624/*
625 * Get a request buffer, fill in standard values, advance pointer.
626 */
627struct ex_msg *
628exgetcbuf(xs, req)
629struct ex_softc *xs;
630int req;
631{
632 register struct ex_msg *bp;
633 struct ifvba *pkb;
634 int s = splimp();
635
636 bp = xs->xs_h2xnext;
19df187f
KS
637 if ((bp->mb_status & MH_OWNER) == MH_EXOS) {
638 splx(s);
639 return (struct ex_msg *)0;
640 }
641 xs->xs_h2xnext = bp->mb_next;
642 bp->mb_1rsrv = 0;
643 bp->mb_rqst = req;
644 bp->mb_length = MBDATALEN;
645 bp->mb_pkb = (struct ifvba *)0;
646 splx(s);
647 return bp;
648}
649
650/*
651 * Process Ethernet receive completion: If input error just drop packet,
652 * otherwise examine packet to determine type. If can't determine length from
653 * type, then have to drop packet, otherwise decapsulate packet based on type
654 * and pass to type-specific higher-level input routine.
655 */
656exrecv(unit, bp)
657int unit;
658register struct ex_msg *bp;
659{
660 register struct ex_softc *xs = &ex_softc[unit];
661 register struct ether_header *eh;
662 register struct mbuf *m;
663 int len, off, resid;
664 register struct ifqueue *inq;
665 int s;
666
667 xs->xs_if.if_ipackets++;
668 /* total length - header - crc */
669 len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
670 if (bp->mb_rply != LL_OK) {
671 if (xs->xs_if.if_ierrors++ % 100 == 0)
672 printf("ex%d: 100 receive errors=%b\n",
673 unit, bp->mb_rply, RECV_BITS);
674 return;
675 }
676 eh = (struct ether_header *)(bp->mb_pkb->iff_buffer);
677
678 /*
679 * Deal with trailer protocol: if type is PUP trailer get true type from
680 * first 16-bit word past data. Remember that type was trailer by
681 * setting off.
682 */
683 eh->ether_type = ntohs((u_short)eh->ether_type);
684#define exdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
685 if (eh->ether_type >= ETHERTYPE_TRAIL &&
686 eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
687 off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
688 if (off >= ETHERMTU)
689 return; /* sanity */
690 eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
691 resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
692 if (off + resid > len)
693 return; /* sanity */
694 len = off + resid;
695 } else
696 off = 0;
697 if (len == 0)
698 return;
699 /*
700 * Pull packet off interface. Off is nonzero if packet
701 * has trailing header; if_vbaget will then force this header
702 * information to be at the front, but we still have to drop
703 * the type and length which are at the front of any trailer data.
704 */
705 m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, &xs->xs_if, 0);
706 if (m == 0)
707 return;
708 ether_input(&xs->xs_if, eh, m);
709 return;
710}
711
712/*
713 * Hang a receive request. This routine is called by exinit and excdint,
714 * with interrupts disabled in both cases.
715 */
716exhangrcv(unit)
717 int unit;
718{
719 register struct ex_softc *xs = &ex_softc[unit];
720 register struct ex_msg *bp;
721 register struct ifvba *pkb;
722 short mustint = 0;
723 union l_util {
724 u_long l;
725 struct i86_long i;
726 } l_util;
727
728 while (xs->xs_nrec < NREC) {
729 if (xs->xs_pkblist == (struct ifvba *)0)
730 break;
731 if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) {
732 break;
733 }
385e8f24
KS
734 GetPkBuf(bp, pkb);
735 pkb->iff_mbuf = 0;
19df187f
KS
736 xs->xs_nrec += 1;
737 bp->mb_er.er_nblock = 1;
738 bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
739 l_util.l = BUSADDR(pkb->iff_buffer);
740 bp->mb_er.er_blks[0].bb_addr = l_util.i;
741 bp->mb_status |= MH_EXOS;
742 mustint = 1;
743 }
744 if (mustint == 0)
745 return;
746 movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT);
747}
748
749/*
750 * Ethernet output routine is ether_output().
751 */
752
753/*
754 * Watchdog routine (currently not used). Might use this to get stats from EXOS.
755 */
756exwatch(unit)
757int unit;
758{
759 struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr;
760 register struct ex_softc *xs = &ex_softc[unit];
761 register struct ex_msg *bp;
762 int s = splimp();
763
764 if (xs->xs_flags & EX_STATPENDING)
765 goto exspnd;
766 if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) {
767 splx(s);
768 return;
769 }
770 xs->xs_flags |= EX_STATPENDING;
771 bp->mb_ns.ns_mask = READ_OBJ;
772 bp->mb_ns.ns_rsrv = 0;
773 bp->mb_ns.ns_nobj = 8;
774 bp->mb_ns.ns_xobj = 0;
775 bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET;
776 bp->mb_status |= MH_EXOS;
777 movow(&exaddr->ex_portb, EX_NTRUPT);
778exspnd: splx(s);
779 xs->xs_if.if_timer = EXWATCHINTVL;
780}
781
782/*
783 * Process an ioctl request.
784 */
785exioctl(ifp, cmd, data)
786 register struct ifnet *ifp;
787 int cmd;
788 caddr_t data;
789{
790 register struct ifaddr *ifa = (struct ifaddr *)data;
791 register struct ex_softc *xs = &ex_softc[ifp->if_unit];
792 int s = splimp(), error = 0;
793
794 switch (cmd) {
795
796 case SIOCSIFADDR:
797 ifp->if_flags |= IFF_UP;
798 exinit(ifp->if_unit);
799
800 switch (ifa->ifa_addr->sa_family) {
801#ifdef INET
802 case AF_INET:
803 ((struct arpcom *)ifp)->ac_ipaddr =
804 IA_SIN(ifa)->sin_addr;
805 arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
806 break;
807#endif
808#ifdef NS
809 case AF_NS:
810 {
811 register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
812
813 if (ns_nullhost(*ina))
814 ina->x_host = *(union ns_host *)(xs->xs_addr);
815 else
816 ex_setaddr(ina->x_host.c_host,ifp->if_unit);
817 break;
818 }
819#endif
820 }
821 break;
822
823 case SIOCSIFFLAGS:
824 if ((ifp->if_flags & IFF_UP) == 0 &&
825 xs->xs_flags & EX_RUNNING) {
826 movow(&((struct exdevice *)
827 (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET);
828 xs->xs_flags &= ~EX_RUNNING;
829 } else if (ifp->if_flags & IFF_UP &&
830 (xs->xs_flags & EX_RUNNING) == 0)
831 exinit(ifp->if_unit);
832 break;
833
834 default:
835 error = EINVAL;
836 }
837 splx(s);
838 return (error);
839}
840
841/*
842 * set ethernet address for unit
843 */
844ex_setaddr(physaddr, unit)
845 u_char *physaddr;
846 int unit;
847{
848 register struct ex_softc *xs = &ex_softc[unit];
19df187f
KS
849
850 if (physaddr) {
851 xs->xs_flags |= EX_SETADDR;
852 bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
853 }
385e8f24
KS
854 ex_setmulti((u_char *)xs->xs_addr, unit, PHYSSLOT);
855}
856
857/*
858 * Enable multicast reception for unit.
859 */
860ex_setmulti(linkaddr, unit, slot)
861 u_char *linkaddr;
862 int unit, slot;
863{
864 register struct ex_softc *xs = &ex_softc[unit];
865 struct vba_device *ui = exinfo[unit];
866 register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
867 register struct ex_msg *bp;
868
869 if (!(xs->xs_flags & EX_RUNNING))
19df187f 870 return;
385e8f24 871 bp = exgetcbuf(xs, LLNET_ADDRS);
19df187f 872 bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
385e8f24
KS
873 bp->mb_na.na_slot = slot;
874 bcopy((caddr_t)linkaddr, (caddr_t)bp->mb_na.na_addrs, 6);
19df187f
KS
875 bp->mb_status |= MH_EXOS;
876 movow(&addr->ex_portb, EX_NTRUPT);
877 bp = xs->xs_x2hnext;
385e8f24 878 while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
19df187f 879#ifdef DEBUG
385e8f24
KS
880 log(LOG_DEBUG, "ex%d: %s %s (slot %d)\n", unit,
881 (slot == PHYSSLOT ? "reset addr" : "add multicast"
882 ether_sprintf(bp->mb_na.na_addrs), slot);
19df187f
KS
883#endif
884 /*
385e8f24 885 * Now, re-enable reception on slot.
19df187f 886 */
385e8f24 887 bp = exgetcbuf(xs, LLNET_RECV);
19df187f 888 bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
385e8f24 889 bp->mb_nr.nr_slot = slot;
19df187f
KS
890 bp->mb_status |= MH_EXOS;
891 movow(&addr->ex_portb, EX_NTRUPT);
892 bp = xs->xs_x2hnext;
385e8f24 893 while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
19df187f
KS
894 ;
895}
896#endif