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