if_vx.c revision 20096
1/* 2 * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Herb Peyerl. 16 * 4. The name of Herb Peyerl may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 */ 31 32/* 33 * Created from if_ep.c driver by Fred Gray (fgray@rice.edu) to support 34 * the 3c590 family. 35 */ 36 37/* 38 * Modified from the FreeBSD 1.1.5.1 version by: 39 * Andres Vega Garcia 40 * INRIA - Sophia Antipolis, France 41 * avega@sophia.inria.fr 42 */ 43 44/* 45 * Promiscuous mode added and interrupt logic slightly changed 46 * to reduce the number of adapter failures. Transceiver select 47 * logic changed to use value from EEPROM. Autoconfiguration 48 * features added. 49 * Done by: 50 * Serge Babkin 51 * Chelindbank (Chelyabinsk, Russia) 52 * babkin@hq.icb.chel.su 53 */ 54 55#include "vx.h" 56#if NVX > 0 57 58#include "bpfilter.h" 59 60#include <sys/param.h> 61#include <sys/systm.h> 62#include <sys/conf.h> 63#include <sys/errno.h> 64#include <sys/ioctl.h> 65#include <sys/mbuf.h> 66#include <sys/socket.h> 67#include <sys/syslog.h> 68 69#include <net/if.h> 70#include <net/if_dl.h> 71#include <net/if_mib.h> 72#include <net/if_types.h> 73 74#ifdef INET 75#include <netinet/in.h> 76#include <netinet/in_systm.h> 77#include <netinet/in_var.h> 78#include <netinet/ip.h> 79#include <netinet/if_ether.h> 80#endif 81 82#ifdef IPX 83#include <netipx/ipx.h> 84#include <netipx/ipx_if.h> 85#endif 86 87#ifdef NS 88#include <netns/ns.h> 89#include <netns/ns_if.h> 90#endif 91 92#if NBPFILTER > 0 93#include <net/bpf.h> 94#include <net/bpfdesc.h> 95#endif 96 97#include <machine/clock.h> 98#include <machine/md_var.h> 99 100#include <dev/vx/if_vxreg.h> 101 102#define ETHER_MAX_LEN 1518 103#define ETHER_ADDR_LEN 6 104 105struct vx_softc *vx_softc[NVX]; 106 107u_long vx_count; /* both PCI and EISA */ 108 109static struct connector_entry { 110 int bit; 111 char *name; 112} conn_tab[VX_CONNECTORS] = { 113#define CONNECTOR_UTP 0 114 { 0x08, "utp"}, 115#define CONNECTOR_AUI 1 116 { 0x20, "aui"}, 117/* dummy */ 118 { 0, "???"}, 119#define CONNECTOR_BNC 3 120 { 0x10, "bnc"}, 121#define CONNECTOR_TX 4 122 { 0x02, "tx"}, 123#define CONNECTOR_FX 5 124 { 0x04, "fx"}, 125#define CONNECTOR_MII 6 126 { 0x40, "mii"}, 127 { 0, "???"} 128}; 129 130/* struct vx_softc *vxalloc __P((int)); */ 131/* void *vxfree __P((struct vx_softc *)); */ 132/* int vxattach __P((struct vx_softc *)); */ 133static void vxtxstat __P((struct vx_softc *)); 134static int vxstatus __P((struct vx_softc *)); 135static void vxinit __P((void *)); 136static int vxioctl __P((struct ifnet *, int, caddr_t)); 137static void vxstart __P((struct ifnet *ifp)); 138static void vxwatchdog __P((struct ifnet *)); 139static void vxreset __P((struct vx_softc *)); 140/* void vxstop __P((struct vx_softc *)); */ 141static void vxread __P((struct vx_softc *)); 142static struct mbuf *vxget __P((struct vx_softc *, u_int)); 143static void vxmbuffill __P((void *)); 144static void vxmbufempty __P((struct vx_softc *)); 145static void vxsetfilter __P((struct vx_softc *)); 146static void vxgetlink __P((struct vx_softc *)); 147static void vxsetlink __P((struct vx_softc *)); 148/* int vxbusyeeprom __P((struct vx_softc *)); */ 149/* void vxintr __P((void *)); */ 150 151struct vx_softc * 152vxalloc(unit) 153 int unit; 154{ 155 struct vx_softc *sc; 156 157 if (unit >= NVX) { 158 printf("vx%d: unit number too high.\n", unit); 159 return NULL; 160 } 161 162 if (vx_softc[unit]) { 163 printf("vx%d: already allocated.\n", unit); 164 return NULL; 165 } 166 167 sc = malloc(sizeof(struct vx_softc), M_DEVBUF, M_NOWAIT); 168 if (sc == NULL) { 169 printf("vx%d: cannot malloc.\n", unit); 170 return NULL; 171 } 172 bzero(sc, sizeof(struct vx_softc)); 173 174 vx_softc[unit] = sc; 175 sc->unit = unit; 176 return (sc); 177} 178 179void 180vxfree(sc) 181 struct vx_softc *sc; 182{ 183 vx_softc[sc->unit] = NULL; 184 free(sc, M_DEVBUF); 185 return; 186} 187 188int 189vxattach(sc) 190 struct vx_softc *sc; 191{ 192 struct ifnet *ifp = &sc->arpcom.ac_if; 193 int i; 194 195 GO_WINDOW(0); 196 outw(VX_COMMAND, GLOBAL_RESET); 197 VX_BUSY_WAIT; 198 199 vxgetlink(sc); 200 201 /* 202 * Read the station address from the eeprom 203 */ 204 GO_WINDOW(0); 205 for (i = 0; i < 3; i++) { 206 int x; 207 if (vxbusyeeprom(sc)) 208 return 0; 209 outw(BASE + VX_W0_EEPROM_COMMAND, EEPROM_CMD_RD 210 | (EEPROM_OEM_ADDR_0 + i)); 211 if (vxbusyeeprom(sc)) 212 return 0; 213 x = inw(BASE + VX_W0_EEPROM_DATA); 214 sc->arpcom.ac_enaddr[(i << 1)] = x >> 8; 215 sc->arpcom.ac_enaddr[(i << 1) + 1] = x; 216 } 217 218 printf(" address %6D\n", sc->arpcom.ac_enaddr, ":"); 219 220 ifp->if_unit = sc->unit; 221 ifp->if_name = "vx"; 222 ifp->if_mtu = ETHERMTU; 223 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 224 ifp->if_output = ether_output; 225 ifp->if_start = vxstart; 226 ifp->if_ioctl = vxioctl; 227 ifp->if_init = vxinit; 228 ifp->if_watchdog = vxwatchdog; 229 ifp->if_softc = sc; 230 231 if_attach(ifp); 232 ether_ifattach(ifp); 233 234#if NBPFILTER > 0 235 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 236#endif 237 238 sc->tx_start_thresh = 20; /* probably a good starting point. */ 239 240 vxstop(sc); 241 242 return 1; 243} 244 245 246 247/* 248 * The order in here seems important. Otherwise we may not receive 249 * interrupts. ?! 250 */ 251static void 252vxinit(xsc) 253 void *xsc; 254{ 255 struct vx_softc *sc = (struct vx_softc *) xsc; 256 struct ifnet *ifp = &sc->arpcom.ac_if; 257 int i; 258 259 VX_BUSY_WAIT; 260 261 GO_WINDOW(2); 262 263 for (i = 0; i < 6; i++) /* Reload the ether_addr. */ 264 outb(BASE + VX_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); 265 266 outw(BASE + VX_COMMAND, RX_RESET); 267 VX_BUSY_WAIT; 268 outw(BASE + VX_COMMAND, TX_RESET); 269 VX_BUSY_WAIT; 270 271 GO_WINDOW(1); /* Window 1 is operating window */ 272 for (i = 0; i < 31; i++) 273 inb(BASE + VX_W1_TX_STATUS); 274 275 outw(BASE + VX_COMMAND,SET_RD_0_MASK | S_CARD_FAILURE | 276 S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 277 outw(BASE + VX_COMMAND,SET_INTR_MASK | S_CARD_FAILURE | 278 S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 279 280 /* 281 * Attempt to get rid of any stray interrupts that occured during 282 * configuration. On the i386 this isn't possible because one may 283 * already be queued. However, a single stray interrupt is 284 * unimportant. 285 */ 286 outw(BASE + VX_COMMAND, ACK_INTR | 0xff); 287 288 vxsetfilter(sc); 289 vxsetlink(sc); 290 291 outw(BASE + VX_COMMAND, RX_ENABLE); 292 outw(BASE + VX_COMMAND, TX_ENABLE); 293 294 vxmbuffill((caddr_t) sc); 295 296 /* Interface is now `running', with no output active. */ 297 ifp->if_flags |= IFF_RUNNING; 298 ifp->if_flags &= ~IFF_OACTIVE; 299 300 /* Attempt to start output, if any. */ 301 vxstart(ifp); 302} 303 304static void 305vxsetfilter(sc) 306 struct vx_softc *sc; 307{ 308 register struct ifnet *ifp = &sc->arpcom.ac_if; 309 310 GO_WINDOW(1); /* Window 1 is operating window */ 311 outw(BASE + VX_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST | 312 ((sc->arpcom.ac_multicnt > 0) ? FIL_MULTICAST : 0 ) | 313 ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0 )); 314} 315 316static void 317vxgetlink(sc) 318 struct vx_softc *sc; 319{ 320 int n, k; 321 322 GO_WINDOW(3); 323 sc->vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f; 324 for (n = 0, k = 0; k < VX_CONNECTORS; k++) { 325 if (sc->vx_connectors & conn_tab[k].bit) { 326 if (n > 0) { 327 printf("/"); 328 } 329 printf(conn_tab[k].name); 330 n++; 331 } 332 } 333 if (sc->vx_connectors == 0) { 334 printf("no connectors!"); 335 return; 336 } 337 GO_WINDOW(3); 338 sc->vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG) 339 & INTERNAL_CONNECTOR_MASK) 340 >> INTERNAL_CONNECTOR_BITS; 341 if (sc->vx_connector & 0x10) { 342 sc->vx_connector &= 0x0f; 343 printf("[*%s*]", conn_tab[sc->vx_connector].name); 344 printf(": disable 'auto select' with DOS util!", sc->unit); 345 } else { 346 printf("[*%s*]", conn_tab[sc->vx_connector].name); 347 } 348} 349 350static void 351vxsetlink(sc) 352 struct vx_softc *sc; 353{ 354 register struct ifnet *ifp = &sc->arpcom.ac_if; 355 int i, j, k; 356 char *reason, *warning; 357 static short prev_flags; 358 static char prev_conn = -1; 359 360 if (prev_conn == -1) { 361 prev_conn = sc->vx_connector; 362 } 363 364 /* 365 * S.B. 366 * 367 * Now behavior was slightly changed: 368 * 369 * if any of flags link[0-2] is used and its connector is 370 * physically present the following connectors are used: 371 * 372 * link0 - AUI * highest precedence 373 * link1 - BNC 374 * link2 - UTP * lowest precedence 375 * 376 * If none of them is specified then 377 * connector specified in the EEPROM is used 378 * (if present on card or UTP if not). 379 */ 380 381 i = sc->vx_connector; /* default in EEPROM */ 382 reason = "default"; 383 warning = 0; 384 385 if (ifp->if_flags & IFF_LINK0) { 386 if (sc->vx_connectors & conn_tab[CONNECTOR_AUI].bit) { 387 i = CONNECTOR_AUI; 388 reason = "link0"; 389 } else { 390 warning = "aui not present! (link0)"; 391 } 392 } else if (ifp->if_flags & IFF_LINK1) { 393 if (sc->vx_connectors & conn_tab[CONNECTOR_BNC].bit) { 394 i = CONNECTOR_BNC; 395 reason = "link1"; 396 } else { 397 warning = "bnc not present! (link1)"; 398 } 399 } else if (ifp->if_flags & IFF_LINK2) { 400 if (sc->vx_connectors & conn_tab[CONNECTOR_UTP].bit) { 401 i = CONNECTOR_UTP; 402 reason = "link2"; 403 } else { 404 warning = "utp not present! (link2)"; 405 } 406 } else if ((sc->vx_connectors & conn_tab[sc->vx_connector].bit) == 0) { 407 warning = "strange connector type in EEPROM."; 408 reason = "forced"; 409 i = CONNECTOR_UTP; 410 } 411 412 /* Avoid unnecessary message. */ 413 k = (prev_flags ^ ifp->if_flags) & (IFF_LINK0 | IFF_LINK1 | IFF_LINK2); 414 if ((k != 0) || (prev_conn != i)) { 415 if (warning != 0) { 416 printf("vx%d: warning: %s\n", sc->unit); 417 } 418 printf("vx%d: selected %s. (%s)\n", 419 sc->unit, conn_tab[i].name, reason); 420 } 421 422 /* Set the selected connector. */ 423 GO_WINDOW(3); 424 j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK; 425 outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS)); 426 427 /* First, disable all. */ 428 outw(BASE + VX_COMMAND, STOP_TRANSCEIVER); 429 DELAY(800); 430 GO_WINDOW(4); 431 outw(BASE + VX_W4_MEDIA_TYPE, 0); 432 433 /* Second, enable the selected one. */ 434 switch(i) { 435 case CONNECTOR_UTP: 436 GO_WINDOW(4); 437 outw(BASE + VX_W4_MEDIA_TYPE, ENABLE_UTP); 438 break; 439 case CONNECTOR_BNC: 440 outw(BASE + VX_COMMAND, START_TRANSCEIVER); 441 DELAY(800); 442 break; 443 case CONNECTOR_TX: 444 case CONNECTOR_FX: 445 GO_WINDOW(4); 446 outw(BASE + VX_W4_MEDIA_TYPE, LINKBEAT_ENABLE); 447 break; 448 default: /* AUI and MII fall here */ 449 break; 450 } 451 GO_WINDOW(1); 452 453 prev_flags = ifp->if_flags; 454 prev_conn = i; 455} 456 457static void 458vxstart(ifp) 459 struct ifnet *ifp; 460{ 461 register struct vx_softc *sc = vx_softc[ifp->if_unit]; 462 register struct mbuf *m, *m0; 463 int sh, len, pad; 464 465 /* Don't transmit if interface is busy or not running */ 466 if ((sc->arpcom.ac_if.if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) 467 return; 468 469startagain: 470 /* Sneak a peek at the next packet */ 471 m0 = ifp->if_snd.ifq_head; 472 if (m0 == 0) { 473 return; 474 } 475 /* We need to use m->m_pkthdr.len, so require the header */ 476 if ((m0->m_flags & M_PKTHDR) == 0) 477 panic("vxstart: no header mbuf"); 478 len = m0->m_pkthdr.len; 479 480 pad = (4 - len) & 3; 481 482 /* 483 * The 3c509 automatically pads short packets to minimum ethernet length, 484 * but we drop packets that are too large. Perhaps we should truncate 485 * them instead? 486 */ 487 if (len + pad > ETHER_MAX_LEN) { 488 /* packet is obviously too large: toss it */ 489 ++ifp->if_oerrors; 490 IF_DEQUEUE(&ifp->if_snd, m0); 491 m_freem(m0); 492 goto readcheck; 493 } 494 VX_BUSY_WAIT; 495 if (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) { 496 outw(BASE + VX_COMMAND, SET_TX_AVAIL_THRESH | ((len + pad + 4) >> 2)); 497 /* not enough room in FIFO */ 498 if (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) { /* make sure */ 499 ifp->if_flags |= IFF_OACTIVE; 500 ifp->if_timer = 1; 501 return; 502 } 503 } 504 outw(BASE + VX_COMMAND, SET_TX_AVAIL_THRESH | (8188 >> 2)); 505 IF_DEQUEUE(&ifp->if_snd, m0); 506 if (m0 == 0) { /* not really needed */ 507 return; 508 } 509 510 VX_BUSY_WAIT; 511 outw(BASE + VX_COMMAND, SET_TX_START_THRESH | 512 ((len / 4 + sc->tx_start_thresh) >> 2)); 513 514#if NBPFILTER > 0 515 if (sc->arpcom.ac_if.if_bpf) { 516 bpf_mtap(&sc->arpcom.ac_if, m0); 517 } 518#endif 519 520 /* 521 * Do the output at splhigh() so that an interrupt from another device 522 * won't cause a FIFO underrun. 523 */ 524 sh = splhigh(); 525 526 outl(BASE + VX_W1_TX_PIO_WR_1, len | TX_INDICATE); 527 528 for (m = m0; m != 0;) { 529 if (m->m_len > 3) 530 outsl(BASE + VX_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4); 531 if (m->m_len & 3) 532 outsb(BASE + VX_W1_TX_PIO_WR_1, 533 mtod(m, caddr_t) + (m->m_len & ~3) , m->m_len & 3); 534 MFREE(m, m0); 535 m = m0; 536 } 537 while (pad--) 538 outb(BASE + VX_W1_TX_PIO_WR_1, 0); /* Padding */ 539 540 splx(sh); 541 542 ++ifp->if_opackets; 543 ifp->if_timer = 1; 544 545readcheck: 546 if ((inw(BASE + VX_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) { 547 /* We received a complete packet. */ 548 549 if ((inw(BASE + VX_STATUS) & S_INTR_LATCH) == 0) { 550 /* 551 * No interrupt, read the packet and continue 552 * Is this supposed to happen? Is my motherboard 553 * completely busted? 554 */ 555 vxread(sc); 556 } else 557 /* Got an interrupt, return so that it gets serviced. */ 558 return; 559 } else { 560 /* Check if we are stuck and reset [see XXX comment] */ 561 if (vxstatus(sc)) { 562 if (ifp->if_flags & IFF_DEBUG) 563 printf("vx%d: adapter reset\n", ifp->if_unit); 564 vxreset(sc); 565 } 566 } 567 568 goto startagain; 569} 570 571/* 572 * XXX: The 3c509 card can get in a mode where both the fifo status bit 573 * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set 574 * We detect this situation and we reset the adapter. 575 * It happens at times when there is a lot of broadcast traffic 576 * on the cable (once in a blue moon). 577 */ 578static int 579vxstatus(sc) 580 struct vx_softc *sc; 581{ 582 int fifost; 583 584 /* 585 * Check the FIFO status and act accordingly 586 */ 587 GO_WINDOW(4); 588 fifost = inw(BASE + VX_W4_FIFO_DIAG); 589 GO_WINDOW(1); 590 591 if (fifost & FIFOS_RX_UNDERRUN) { 592 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 593 printf("vx%d: RX underrun\n", sc->unit); 594 vxreset(sc); 595 return 0; 596 } 597 598 if (fifost & FIFOS_RX_STATUS_OVERRUN) { 599 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 600 printf("vx%d: RX Status overrun\n", sc->unit); 601 return 1; 602 } 603 604 if (fifost & FIFOS_RX_OVERRUN) { 605 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 606 printf("vx%d: RX overrun\n", sc->unit); 607 return 1; 608 } 609 610 if (fifost & FIFOS_TX_OVERRUN) { 611 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 612 printf("vx%d: TX overrun\n", sc->unit); 613 vxreset(sc); 614 return 0; 615 } 616 617 return 0; 618} 619 620static void 621vxtxstat(sc) 622 struct vx_softc *sc; 623{ 624 int i; 625 626 /* 627 * We need to read+write TX_STATUS until we get a 0 status 628 * in order to turn off the interrupt flag. 629 */ 630 while ((i = inb(BASE + VX_W1_TX_STATUS)) & TXS_COMPLETE) { 631 outb(BASE + VX_W1_TX_STATUS, 0x0); 632 633 if (i & TXS_JABBER) { 634 ++sc->arpcom.ac_if.if_oerrors; 635 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 636 printf("vx%d: jabber (%x)\n", sc->unit, i); 637 vxreset(sc); 638 } else if (i & TXS_UNDERRUN) { 639 ++sc->arpcom.ac_if.if_oerrors; 640 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 641 printf("vx%d: fifo underrun (%x) @%d\n", 642 sc->unit, i, sc->tx_start_thresh); 643 if (sc->tx_succ_ok < 100) 644 sc->tx_start_thresh = min(ETHER_MAX_LEN, sc->tx_start_thresh + 20); 645 sc->tx_succ_ok = 0; 646 vxreset(sc); 647 } else if (i & TXS_MAX_COLLISION) { 648 ++sc->arpcom.ac_if.if_collisions; 649 outw(BASE + VX_COMMAND, TX_ENABLE); 650 sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 651 } else 652 sc->tx_succ_ok = (sc->tx_succ_ok+1) & 127; 653 } 654} 655 656void 657vxintr(sc) 658 struct vx_softc *sc; 659{ 660 register short status; 661 struct ifnet *ifp = &sc->arpcom.ac_if; 662 663 for (;;) { 664 outw(BASE + VX_COMMAND, C_INTR_LATCH); 665 666 status = inw(BASE + VX_STATUS); 667 668 if ((status & (S_TX_COMPLETE | S_TX_AVAIL | 669 S_RX_COMPLETE | S_CARD_FAILURE)) == 0) 670 break; 671 672 /* 673 * Acknowledge any interrupts. It's important that we do this 674 * first, since there would otherwise be a race condition. 675 * Due to the i386 interrupt queueing, we may get spurious 676 * interrupts occasionally. 677 */ 678 outw(BASE + VX_COMMAND, ACK_INTR | status); 679 680 if (status & S_RX_COMPLETE) 681 vxread(sc); 682 if (status & S_TX_AVAIL) { 683 ifp->if_timer = 0; 684 sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 685 vxstart(&sc->arpcom.ac_if); 686 } 687 if (status & S_CARD_FAILURE) { 688 printf("vx%d: adapter failure (%x)\n", sc->unit, status); 689 ifp->if_timer = 0; 690 vxreset(sc); 691 return; 692 } 693 if (status & S_TX_COMPLETE) { 694 ifp->if_timer = 0; 695 vxtxstat(sc); 696 vxstart(ifp); 697 } 698 } 699 700 /* no more interrupts */ 701 return; 702} 703 704static void 705vxread(sc) 706 struct vx_softc *sc; 707{ 708 struct ifnet *ifp = &sc->arpcom.ac_if; 709 struct mbuf *m; 710 struct ether_header *eh; 711 u_int len; 712 713 len = inw(BASE + VX_W1_RX_STATUS); 714 715again: 716 717 if (ifp->if_flags & IFF_DEBUG) { 718 int err = len & ERR_MASK; 719 char *s = NULL; 720 721 if (len & ERR_INCOMPLETE) 722 s = "incomplete packet"; 723 else if (err == ERR_OVERRUN) 724 s = "packet overrun"; 725 else if (err == ERR_RUNT) 726 s = "runt packet"; 727 else if (err == ERR_ALIGNMENT) 728 s = "bad alignment"; 729 else if (err == ERR_CRC) 730 s = "bad crc"; 731 else if (err == ERR_OVERSIZE) 732 s = "oversized packet"; 733 else if (err == ERR_DRIBBLE) 734 s = "dribble bits"; 735 736 if (s) 737 printf("vx%d: %s\n", sc->unit, s); 738 } 739 740 if (len & ERR_INCOMPLETE) 741 return; 742 743 if (len & ERR_RX) { 744 ++ifp->if_ierrors; 745 goto abort; 746 } 747 748 len &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ 749 750 /* Pull packet off interface. */ 751 m = vxget(sc, len); 752 if (m == 0) { 753 ifp->if_ierrors++; 754 goto abort; 755 } 756 757 ++ifp->if_ipackets; 758 759 /* We assume the header fit entirely in one mbuf. */ 760 eh = mtod(m, struct ether_header *); 761 762#if NBPFILTER > 0 763 /* 764 * Check if there's a BPF listener on this interface. 765 * If so, hand off the raw packet to BPF. 766 */ 767 if (sc->arpcom.ac_if.if_bpf) { 768 bpf_mtap(&sc->arpcom.ac_if, m); 769 770 /* 771 * Note that the interface cannot be in promiscuous mode if 772 * there are no BPF listeners. And if we are in promiscuous 773 * mode, we have to check if this packet is really ours. 774 */ 775 if ((ifp->if_flags & IFF_PROMISC) && 776 (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ 777 bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 778 sizeof(eh->ether_dhost)) != 0) { 779 m_freem(m); 780 return; 781 } 782 } 783#endif 784 785 /* We assume the header fit entirely in one mbuf. */ 786 m_adj(m, sizeof(struct ether_header)); 787 ether_input(ifp, eh, m); 788 789 790 791 /* 792 * In periods of high traffic we can actually receive enough 793 * packets so that the fifo overrun bit will be set at this point, 794 * even though we just read a packet. In this case we 795 * are not going to receive any more interrupts. We check for 796 * this condition and read again until the fifo is not full. 797 * We could simplify this test by not using vxstatus(), but 798 * rechecking the RX_STATUS register directly. This test could 799 * result in unnecessary looping in cases where there is a new 800 * packet but the fifo is not full, but it will not fix the 801 * stuck behavior. 802 * 803 * Even with this improvement, we still get packet overrun errors 804 * which are hurting performance. Maybe when I get some more time 805 * I'll modify vxread() so that it can handle RX_EARLY interrupts. 806 */ 807 if (vxstatus(sc)) { 808 len = inw(BASE + VX_W1_RX_STATUS); 809 /* Check if we are stuck and reset [see XXX comment] */ 810 if (len & ERR_INCOMPLETE) { 811 if (ifp->if_flags & IFF_DEBUG) 812 printf("vx%d: adapter reset\n", sc->unit); 813 vxreset(sc); 814 return; 815 } 816 goto again; 817 } 818 819 return; 820 821abort: 822 outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK); 823} 824 825static struct mbuf * 826vxget(sc, totlen) 827 struct vx_softc *sc; 828 u_int totlen; 829{ 830 struct ifnet *ifp = &sc->arpcom.ac_if; 831 struct mbuf *top, **mp, *m; 832 int len; 833 int sh; 834 835 m = sc->mb[sc->next_mb]; 836 sc->mb[sc->next_mb] = 0; 837 if (m == 0) { 838 MGETHDR(m, M_DONTWAIT, MT_DATA); 839 if (m == 0) 840 return 0; 841 } else { 842 /* If the queue is no longer full, refill. */ 843 if (sc->last_mb == sc->next_mb) 844 timeout(vxmbuffill, sc, 1); 845 /* Convert one of our saved mbuf's. */ 846 sc->next_mb = (sc->next_mb + 1) % MAX_MBS; 847 m->m_data = m->m_pktdat; 848 m->m_flags = M_PKTHDR; 849 } 850 m->m_pkthdr.rcvif = ifp; 851 m->m_pkthdr.len = totlen; 852 len = MHLEN; 853 top = 0; 854 mp = ⊤ 855 856 /* 857 * We read the packet at splhigh() so that an interrupt from another 858 * device doesn't cause the card's buffer to overflow while we're 859 * reading it. We may still lose packets at other times. 860 */ 861 sh = splhigh(); 862 863 while (totlen > 0) { 864 if (top) { 865 m = sc->mb[sc->next_mb]; 866 sc->mb[sc->next_mb] = 0; 867 if (m == 0) { 868 MGET(m, M_DONTWAIT, MT_DATA); 869 if (m == 0) { 870 splx(sh); 871 m_freem(top); 872 return 0; 873 } 874 } else { 875 sc->next_mb = (sc->next_mb + 1) % MAX_MBS; 876 } 877 len = MLEN; 878 } 879 if (totlen >= MINCLSIZE) { 880 MCLGET(m, M_DONTWAIT); 881 if (m->m_flags & M_EXT) 882 len = MCLBYTES; 883 } 884 len = min(totlen, len); 885 if (len > 3) { 886 len &= ~3; 887 insl(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), 888 len / 4); 889 } else 890 insb(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *), 891 len); 892 m->m_len = len; 893 totlen -= len; 894 *mp = m; 895 mp = &m->m_next; 896 } 897 898 outw(BASE +VX_COMMAND, RX_DISCARD_TOP_PACK); 899 900 splx(sh); 901 902 return top; 903} 904 905 906static int 907vxioctl(ifp, cmd, data) 908 register struct ifnet *ifp; 909 int cmd; 910 caddr_t data; 911{ 912 struct vx_softc *sc = vx_softc[ifp->if_unit]; 913 struct ifaddr *ifa = (struct ifaddr *) data; 914 struct ifreq *ifr = (struct ifreq *) data; 915 int s, error = 0; 916 917 s = splimp(); 918 919 switch (cmd) { 920 case SIOCSIFADDR: 921 case SIOCGIFADDR: 922 ether_ioctl(ifp, cmd, data); 923 break; 924 925 case SIOCSIFFLAGS: 926 if ((ifp->if_flags & IFF_UP) == 0 && 927 (ifp->if_flags & IFF_RUNNING) != 0) { 928 /* 929 * If interface is marked up and it is stopped, then 930 * start it. 931 */ 932 vxstop(sc); 933 ifp->if_flags &= ~IFF_RUNNING; 934 } else if ((ifp->if_flags & IFF_UP) != 0 && 935 (ifp->if_flags & IFF_RUNNING) == 0) { 936 /* 937 * If interface is marked up and it is stopped, then 938 * start it. 939 */ 940 vxinit(sc); 941 } else { 942 /* 943 * deal with flags changes: 944 * IFF_MULTICAST, IFF_PROMISC, 945 * IFF_LINK0, IFF_LINK1, 946 */ 947 vxsetfilter(sc); 948 vxsetlink(sc); 949 } 950 break; 951 952 case SIOCSIFMTU: 953 /* 954 * Set the interface MTU. 955 */ 956 if (ifr->ifr_mtu > ETHERMTU) { 957 error = EINVAL; 958 } else { 959 ifp->if_mtu = ifr->ifr_mtu; 960 } 961 break; 962 963 case SIOCADDMULTI: 964 case SIOCDELMULTI: 965 error = ((u_int) cmd == SIOCADDMULTI) ? 966 ether_addmulti(ifr, &sc->arpcom) : 967 ether_delmulti(ifr, &sc->arpcom); 968 969 if (error == ENETRESET) { 970 /* 971 * Multicast list has changed; set the hardware filter 972 * accordingly. 973 */ 974 vxreset(sc); 975 error = 0; 976 } 977 break; 978 979 980 default: 981 error = EINVAL; 982 } 983 984 splx(s); 985 986 return (error); 987} 988 989static void 990vxreset(sc) 991 struct vx_softc *sc; 992{ 993 int s; 994 s = splimp(); 995 996 vxstop(sc); 997 vxinit(sc); 998 splx(s); 999} 1000 1001static void 1002vxwatchdog(ifp) 1003 struct ifnet *ifp; 1004{ 1005 struct vx_softc *sc = vx_softc[ifp->if_unit]; 1006 1007 if (ifp->if_flags & IFF_DEBUG) 1008 printf("vx%d: device timeout\n", ifp->if_unit); 1009 ifp->if_flags &= ~IFF_OACTIVE; 1010 vxstart(ifp); 1011 vxintr(sc); 1012} 1013 1014void 1015vxstop(sc) 1016 struct vx_softc *sc; 1017{ 1018 struct ifnet *ifp = &sc->arpcom.ac_if; 1019 1020 ifp->if_timer = 0; 1021 1022 outw(BASE + VX_COMMAND, RX_DISABLE); 1023 outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK); 1024 VX_BUSY_WAIT; 1025 outw(BASE + VX_COMMAND, TX_DISABLE); 1026 outw(BASE + VX_COMMAND, STOP_TRANSCEIVER); 1027 DELAY(800); 1028 outw(BASE + VX_COMMAND, RX_RESET); 1029 VX_BUSY_WAIT; 1030 outw(BASE + VX_COMMAND, TX_RESET); 1031 VX_BUSY_WAIT; 1032 outw(BASE + VX_COMMAND, C_INTR_LATCH); 1033 outw(BASE + VX_COMMAND, SET_RD_0_MASK); 1034 outw(BASE + VX_COMMAND, SET_INTR_MASK); 1035 outw(BASE + VX_COMMAND, SET_RX_FILTER); 1036 1037 vxmbufempty(sc); 1038} 1039 1040int 1041vxbusyeeprom(sc) 1042 struct vx_softc *sc; 1043{ 1044 int j, i = 100; 1045 1046 while (i--) { 1047 j = inw(BASE + VX_W0_EEPROM_COMMAND); 1048 if (j & EEPROM_BUSY) 1049 DELAY(100); 1050 else 1051 break; 1052 } 1053 if (!i) { 1054 printf("vx%d: eeprom failed to come ready\n", sc->unit); 1055 return (1); 1056 } 1057 return (0); 1058} 1059 1060static void 1061vxmbuffill(sp) 1062 void *sp; 1063{ 1064 struct vx_softc *sc = (struct vx_softc *) sp; 1065 int s, i; 1066 1067 s = splimp(); 1068 i = sc->last_mb; 1069 do { 1070 if (sc->mb[i] == NULL) 1071 MGET(sc->mb[i], M_DONTWAIT, MT_DATA); 1072 if (sc->mb[i] == NULL) 1073 break; 1074 i = (i + 1) % MAX_MBS; 1075 } while (i != sc->next_mb); 1076 sc->last_mb = i; 1077 /* If the queue was not filled, try again. */ 1078 if (sc->last_mb != sc->next_mb) 1079 timeout(vxmbuffill, sc, 1); 1080 splx(s); 1081} 1082 1083static void 1084vxmbufempty(sc) 1085 struct vx_softc *sc; 1086{ 1087 int s, i; 1088 1089 s = splimp(); 1090 for (i = 0; i < MAX_MBS; i++) { 1091 if (sc->mb[i]) { 1092 m_freem(sc->mb[i]); 1093 sc->mb[i] = NULL; 1094 } 1095 } 1096 sc->last_mb = sc->next_mb = 0; 1097 untimeout(vxmbuffill, sc); 1098 splx(s); 1099} 1100 1101#endif /* NVX > 0 */ 1102