if_ex.c revision 119418
1/* 2 * Copyright (c) 1996, Javier Mart�n Rueda (jmrueda@diatel.upm.es) 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 unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * 28 * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 29 * <mdodd@FreeBSD.org> 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/dev/ex/if_ex.c 119418 2003-08-24 17:55:58Z obrien $"); 34 35/* 36 * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 37 * 38 * Revision history: 39 * 40 * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver. 41 * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 42 */ 43 44#include <sys/param.h> 45#include <sys/systm.h> 46#include <sys/kernel.h> 47#include <sys/sockio.h> 48#include <sys/mbuf.h> 49#include <sys/socket.h> 50 51#include <sys/module.h> 52#include <sys/bus.h> 53 54#include <machine/bus.h> 55#include <machine/resource.h> 56#include <sys/rman.h> 57 58#include <net/if.h> 59#include <net/if_arp.h> 60#include <net/if_dl.h> 61#include <net/if_media.h> 62#include <net/ethernet.h> 63#include <net/bpf.h> 64 65#include <netinet/in.h> 66#include <netinet/if_ether.h> 67 68 69#include <isa/isavar.h> 70#include <isa/pnpvar.h> 71 72#include <dev/ex/if_exreg.h> 73#include <dev/ex/if_exvar.h> 74 75#ifdef EXDEBUG 76# define Start_End 1 77# define Rcvd_Pkts 2 78# define Sent_Pkts 4 79# define Status 8 80static int debug_mask = 0; 81static int exintr_count = 0; 82# define DODEBUG(level, action) if (level & debug_mask) action 83#else 84# define DODEBUG(level, action) 85#endif 86 87devclass_t ex_devclass; 88 89char irq2eemap[] = 90 { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 91u_char ee2irqmap[] = 92 { 9, 3, 5, 10, 11, 0, 0, 0 }; 93 94char plus_irq2eemap[] = 95 { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 96u_char plus_ee2irqmap[] = 97 { 3, 4, 5, 7, 9, 10, 11, 12 }; 98 99/* Network Interface Functions */ 100static void ex_init (void *); 101static void ex_start (struct ifnet *); 102static int ex_ioctl (struct ifnet *, u_long, caddr_t); 103static void ex_watchdog (struct ifnet *); 104 105/* ifmedia Functions */ 106static int ex_ifmedia_upd (struct ifnet *); 107static void ex_ifmedia_sts (struct ifnet *, struct ifmediareq *); 108 109static int ex_get_media (u_int32_t iobase); 110 111static void ex_reset (struct ex_softc *); 112static void ex_setmulti (struct ex_softc *); 113 114static void ex_tx_intr (struct ex_softc *); 115static void ex_rx_intr (struct ex_softc *); 116 117int 118look_for_card (u_int32_t iobase) 119{ 120 int count1, count2; 121 122 /* 123 * Check for the i82595 signature, and check that the round robin 124 * counter actually advances. 125 */ 126 if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 127 return(0); 128 count2 = inb(iobase + ID_REG); 129 count2 = inb(iobase + ID_REG); 130 count2 = inb(iobase + ID_REG); 131 132 return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 133} 134 135void 136ex_get_address (u_int32_t iobase, u_char *enaddr) 137{ 138 u_int16_t eaddr_tmp; 139 140 eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 141 enaddr[5] = eaddr_tmp & 0xff; 142 enaddr[4] = eaddr_tmp >> 8; 143 eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 144 enaddr[3] = eaddr_tmp & 0xff; 145 enaddr[2] = eaddr_tmp >> 8; 146 eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 147 enaddr[1] = eaddr_tmp & 0xff; 148 enaddr[0] = eaddr_tmp >> 8; 149 150 return; 151} 152 153int 154ex_card_type (u_char *enaddr) 155{ 156 if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 157 return (CARD_TYPE_EX_10_PLUS); 158 159 return (CARD_TYPE_EX_10); 160} 161 162/* 163 * Caller is responsible for eventually calling 164 * ex_release_resources() on failure. 165 */ 166int 167ex_alloc_resources (device_t dev) 168{ 169 struct ex_softc * sc = device_get_softc(dev); 170 int error = 0; 171 172 sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->ioport_rid, 173 0, ~0, 1, RF_ACTIVE); 174 if (!sc->ioport) { 175 device_printf(dev, "No I/O space?!\n"); 176 error = ENOMEM; 177 goto bad; 178 } 179 180 sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 181 0, ~0, 1, RF_ACTIVE); 182 183 if (!sc->irq) { 184 device_printf(dev, "No IRQ?!\n"); 185 error = ENOMEM; 186 goto bad; 187 } 188 189bad: 190 return (error); 191} 192 193void 194ex_release_resources (device_t dev) 195{ 196 struct ex_softc * sc = device_get_softc(dev); 197 198 if (sc->ih) { 199 bus_teardown_intr(dev, sc->irq, sc->ih); 200 sc->ih = NULL; 201 } 202 203 if (sc->ioport) { 204 bus_release_resource(dev, SYS_RES_IOPORT, 205 sc->ioport_rid, sc->ioport); 206 sc->ioport = NULL; 207 } 208 209 if (sc->irq) { 210 bus_release_resource(dev, SYS_RES_IRQ, 211 sc->irq_rid, sc->irq); 212 sc->irq = NULL; 213 } 214 215 return; 216} 217 218int 219ex_attach(device_t dev) 220{ 221 struct ex_softc * sc = device_get_softc(dev); 222 struct ifnet * ifp = &sc->arpcom.ac_if; 223 struct ifmedia * ifm; 224 int unit = device_get_unit(dev); 225 u_int16_t temp; 226 227 /* work out which set of irq <-> internal tables to use */ 228 if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 229 sc->irq2ee = plus_irq2eemap; 230 sc->ee2irq = plus_ee2irqmap; 231 } else { 232 sc->irq2ee = irq2eemap; 233 sc->ee2irq = ee2irqmap; 234 } 235 236 sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 237 238 /* 239 * Initialize the ifnet structure. 240 */ 241 ifp->if_softc = sc; 242 ifp->if_unit = unit; 243 ifp->if_name = "ex"; 244 ifp->if_mtu = ETHERMTU; 245 ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 246 ifp->if_output = ether_output; 247 ifp->if_start = ex_start; 248 ifp->if_ioctl = ex_ioctl; 249 ifp->if_watchdog = ex_watchdog; 250 ifp->if_init = ex_init; 251 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 252 253 ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 254 255 temp = eeprom_read(sc->iobase, EE_W5); 256 if (temp & EE_W5_PORT_TPE) 257 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 258 if (temp & EE_W5_PORT_BNC) 259 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 260 if (temp & EE_W5_PORT_AUI) 261 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 262 263 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 264 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); 265 ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase)); 266 267 ifm = &sc->ifmedia; 268 ifm->ifm_media = ifm->ifm_cur->ifm_media; 269 ex_ifmedia_upd(ifp); 270 271 /* 272 * Attach the interface. 273 */ 274 ether_ifattach(ifp, sc->arpcom.ac_enaddr); 275 276 device_printf(sc->dev, "Ethernet address %6D\n", 277 sc->arpcom.ac_enaddr, ":"); 278 279 return(0); 280} 281 282int 283ex_detach (device_t dev) 284{ 285 struct ex_softc *sc; 286 struct ifnet *ifp; 287 288 sc = device_get_softc(dev); 289 ifp = &sc->arpcom.ac_if; 290 291 ex_stop(sc); 292 293 ifp->if_flags &= ~IFF_RUNNING; 294 ether_ifdetach(ifp); 295 296 ex_release_resources(dev); 297 298 return (0); 299} 300 301static void 302ex_init(void *xsc) 303{ 304 struct ex_softc * sc = (struct ex_softc *) xsc; 305 struct ifnet * ifp = &sc->arpcom.ac_if; 306 int s; 307 int i; 308 register int iobase = sc->iobase; 309 unsigned short temp_reg; 310 311 DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 312 313 if (TAILQ_FIRST(&ifp->if_addrhead) == NULL) { 314 return; 315 } 316 s = splimp(); 317 ifp->if_timer = 0; 318 319 /* 320 * Load the ethernet address into the card. 321 */ 322 outb(iobase + CMD_REG, Bank2_Sel); 323 temp_reg = inb(iobase + EEPROM_REG); 324 if (temp_reg & Trnoff_Enable) { 325 outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 326 } 327 for (i = 0; i < ETHER_ADDR_LEN; i++) { 328 outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 329 } 330 /* 331 * - Setup transmit chaining and discard bad received frames. 332 * - Match broadcast. 333 * - Clear test mode. 334 * - Set receiving mode. 335 * - Set IRQ number. 336 */ 337 outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 338 outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 339 outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 340 outb(iobase + CMD_REG, Bank1_Sel); 341 outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 342 343 /* 344 * Divide the available memory in the card into rcv and xmt buffers. 345 * By default, I use the first 3/4 of the memory for the rcv buffer, 346 * and the remaining 1/4 of the memory for the xmt buffer. 347 */ 348 sc->rx_mem_size = sc->mem_size * 3 / 4; 349 sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 350 sc->rx_lower_limit = 0x0000; 351 sc->rx_upper_limit = sc->rx_mem_size - 2; 352 sc->tx_lower_limit = sc->rx_mem_size; 353 sc->tx_upper_limit = sc->mem_size - 2; 354 outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 355 outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 356 outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 357 outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 358 359 /* 360 * Enable receive and transmit interrupts, and clear any pending int. 361 */ 362 outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 363 outb(iobase + CMD_REG, Bank0_Sel); 364 outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 365 outb(iobase + STATUS_REG, All_Int); 366 367 /* 368 * Initialize receive and transmit ring buffers. 369 */ 370 outw(iobase + RCV_BAR, sc->rx_lower_limit); 371 sc->rx_head = sc->rx_lower_limit; 372 outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 373 outw(iobase + XMT_BAR, sc->tx_lower_limit); 374 sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 375 376 ifp->if_flags |= IFF_RUNNING; 377 ifp->if_flags &= ~IFF_OACTIVE; 378 DODEBUG(Status, printf("OIDLE init\n");); 379 380 ex_setmulti(sc); 381 382 /* 383 * Final reset of the board, and enable operation. 384 */ 385 outb(iobase + CMD_REG, Sel_Reset_CMD); 386 DELAY(2); 387 outb(iobase + CMD_REG, Rcv_Enable_CMD); 388 389 ex_start(ifp); 390 splx(s); 391 392 DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 393} 394 395 396static void 397ex_start(struct ifnet *ifp) 398{ 399 struct ex_softc * sc = ifp->if_softc; 400 int iobase = sc->iobase; 401 int i, s, len, data_len, avail, dest, next; 402 unsigned char tmp16[2]; 403 struct mbuf * opkt; 404 struct mbuf * m; 405 406 DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 407 408 s = splimp(); 409 410 /* 411 * Main loop: send outgoing packets to network card until there are no 412 * more packets left, or the card cannot accept any more yet. 413 */ 414 while (((opkt = ifp->if_snd.ifq_head) != NULL) && 415 !(ifp->if_flags & IFF_OACTIVE)) { 416 417 /* 418 * Ensure there is enough free transmit buffer space for 419 * this packet, including its header. Note: the header 420 * cannot wrap around the end of the transmit buffer and 421 * must be kept together, so we allow space for twice the 422 * length of the header, just in case. 423 */ 424 425 for (len = 0, m = opkt; m != NULL; m = m->m_next) { 426 len += m->m_len; 427 } 428 429 data_len = len; 430 431 DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 432 433 if (len & 1) { 434 len += XMT_HEADER_LEN + 1; 435 } else { 436 len += XMT_HEADER_LEN; 437 } 438 439 if ((i = sc->tx_tail - sc->tx_head) >= 0) { 440 avail = sc->tx_mem_size - i; 441 } else { 442 avail = -i; 443 } 444 445 DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 446 447 if (avail >= len + XMT_HEADER_LEN) { 448 IF_DEQUEUE(&ifp->if_snd, opkt); 449 450#ifdef EX_PSA_INTR 451 /* 452 * Disable rx and tx interrupts, to avoid corruption 453 * of the host address register by interrupt service 454 * routines. 455 * XXX Is this necessary with splimp() enabled? 456 */ 457 outb(iobase + MASK_REG, All_Int); 458#endif 459 460 /* 461 * Compute the start and end addresses of this 462 * frame in the tx buffer. 463 */ 464 dest = sc->tx_tail; 465 next = dest + len; 466 467 if (next > sc->tx_upper_limit) { 468 if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 469 XMT_HEADER_LEN) { 470 dest = sc->tx_lower_limit; 471 next = dest + len; 472 } else { 473 next = sc->tx_lower_limit + 474 next - sc->tx_upper_limit - 2; 475 } 476 } 477 478 /* 479 * Build the packet frame in the card's ring buffer. 480 */ 481 DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 482 483 outw(iobase + HOST_ADDR_REG, dest); 484 outw(iobase + IO_PORT_REG, Transmit_CMD); 485 outw(iobase + IO_PORT_REG, 0); 486 outw(iobase + IO_PORT_REG, next); 487 outw(iobase + IO_PORT_REG, data_len); 488 489 /* 490 * Output the packet data to the card. Ensure all 491 * transfers are 16-bit wide, even if individual 492 * mbufs have odd length. 493 */ 494 495 for (m = opkt, i = 0; m != NULL; m = m->m_next) { 496 DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 497 if (i) { 498 tmp16[1] = *(mtod(m, caddr_t)); 499 outsw(iobase + IO_PORT_REG, tmp16, 1); 500 } 501 outsw(iobase + IO_PORT_REG, 502 mtod(m, caddr_t) + i, (m->m_len - i) / 2); 503 504 if ((i = (m->m_len - i) & 1) != 0) { 505 tmp16[0] = *(mtod(m, caddr_t) + 506 m->m_len - 1); 507 } 508 } 509 if (i) { 510 outsw(iobase + IO_PORT_REG, tmp16, 1); 511 } 512 513 /* 514 * If there were other frames chained, update the 515 * chain in the last one. 516 */ 517 if (sc->tx_head != sc->tx_tail) { 518 if (sc->tx_tail != dest) { 519 outw(iobase + HOST_ADDR_REG, 520 sc->tx_last + XMT_Chain_Point); 521 outw(iobase + IO_PORT_REG, dest); 522 } 523 outw(iobase + HOST_ADDR_REG, 524 sc->tx_last + XMT_Byte_Count); 525 i = inw(iobase + IO_PORT_REG); 526 outw(iobase + HOST_ADDR_REG, 527 sc->tx_last + XMT_Byte_Count); 528 outw(iobase + IO_PORT_REG, i | Ch_bit); 529 } 530 531 /* 532 * Resume normal operation of the card: 533 * - Make a dummy read to flush the DRAM write 534 * pipeline. 535 * - Enable receive and transmit interrupts. 536 * - Send Transmit or Resume_XMT command, as 537 * appropriate. 538 */ 539 inw(iobase + IO_PORT_REG); 540#ifdef EX_PSA_INTR 541 outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 542#endif 543 if (sc->tx_head == sc->tx_tail) { 544 outw(iobase + XMT_BAR, dest); 545 outb(iobase + CMD_REG, Transmit_CMD); 546 sc->tx_head = dest; 547 DODEBUG(Sent_Pkts, printf("Transmit\n");); 548 } else { 549 outb(iobase + CMD_REG, Resume_XMT_List_CMD); 550 DODEBUG(Sent_Pkts, printf("Resume\n");); 551 } 552 553 sc->tx_last = dest; 554 sc->tx_tail = next; 555 556 BPF_MTAP(ifp, opkt); 557 558 ifp->if_timer = 2; 559 ifp->if_opackets++; 560 m_freem(opkt); 561 } else { 562 ifp->if_flags |= IFF_OACTIVE; 563 DODEBUG(Status, printf("OACTIVE start\n");); 564 } 565 } 566 567 splx(s); 568 569 DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 570} 571 572void 573ex_stop(struct ex_softc *sc) 574{ 575 int iobase = sc->iobase; 576 577 DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 578 579 /* 580 * Disable card operation: 581 * - Disable the interrupt line. 582 * - Flush transmission and disable reception. 583 * - Mask and clear all interrupts. 584 * - Reset the 82595. 585 */ 586 outb(iobase + CMD_REG, Bank1_Sel); 587 outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 588 outb(iobase + CMD_REG, Bank0_Sel); 589 outb(iobase + CMD_REG, Rcv_Stop); 590 sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 591 sc->tx_last = 0; /* XXX I think these two lines are not necessary, because ex_init will always be called again to reinit the interface. */ 592 outb(iobase + MASK_REG, All_Int); 593 outb(iobase + STATUS_REG, All_Int); 594 outb(iobase + CMD_REG, Reset_CMD); 595 DELAY(200); 596 597 DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 598 599 return; 600} 601 602void 603ex_intr(void *arg) 604{ 605 struct ex_softc * sc = (struct ex_softc *)arg; 606 struct ifnet * ifp = &sc->arpcom.ac_if; 607 int iobase = sc->iobase; 608 int int_status, send_pkts; 609 610 DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 611 612#ifdef EXDEBUG 613 if (++exintr_count != 1) 614 printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 615#endif 616 617 send_pkts = 0; 618 while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 619 if (int_status & Rx_Int) { 620 outb(iobase + STATUS_REG, Rx_Int); 621 622 ex_rx_intr(sc); 623 } else if (int_status & Tx_Int) { 624 outb(iobase + STATUS_REG, Tx_Int); 625 626 ex_tx_intr(sc); 627 send_pkts = 1; 628 } 629 } 630 631 /* 632 * If any packet has been transmitted, and there are queued packets to 633 * be sent, attempt to send more packets to the network card. 634 */ 635 636 if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { 637 ex_start(ifp); 638 } 639 640#ifdef EXDEBUG 641 exintr_count--; 642#endif 643 644 DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 645 646 return; 647} 648 649static void 650ex_tx_intr(struct ex_softc *sc) 651{ 652 struct ifnet * ifp = &sc->arpcom.ac_if; 653 int iobase = sc->iobase; 654 int tx_status; 655 656 DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 657 658 /* 659 * - Cancel the watchdog. 660 * For all packets transmitted since last transmit interrupt: 661 * - Advance chain pointer to next queued packet. 662 * - Update statistics. 663 */ 664 665 ifp->if_timer = 0; 666 667 while (sc->tx_head != sc->tx_tail) { 668 outw(iobase + HOST_ADDR_REG, sc->tx_head); 669 670 if (! inw(iobase + IO_PORT_REG) & Done_bit) 671 break; 672 673 tx_status = inw(iobase + IO_PORT_REG); 674 sc->tx_head = inw(iobase + IO_PORT_REG); 675 676 if (tx_status & TX_OK_bit) { 677 ifp->if_opackets++; 678 } else { 679 ifp->if_oerrors++; 680 } 681 682 ifp->if_collisions += tx_status & No_Collisions_bits; 683 } 684 685 /* 686 * The card should be ready to accept more packets now. 687 */ 688 689 ifp->if_flags &= ~IFF_OACTIVE; 690 691 DODEBUG(Status, printf("OIDLE tx_intr\n");); 692 DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 693 694 return; 695} 696 697static void 698ex_rx_intr(struct ex_softc *sc) 699{ 700 struct ifnet * ifp = &sc->arpcom.ac_if; 701 int iobase = sc->iobase; 702 int rx_status; 703 int pkt_len; 704 int QQQ; 705 struct mbuf * m; 706 struct mbuf * ipkt; 707 struct ether_header * eh; 708 709 DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 710 711 /* 712 * For all packets received since last receive interrupt: 713 * - If packet ok, read it into a new mbuf and queue it to interface, 714 * updating statistics. 715 * - If packet bad, just discard it, and update statistics. 716 * Finally, advance receive stop limit in card's memory to new location. 717 */ 718 719 outw(iobase + HOST_ADDR_REG, sc->rx_head); 720 721 while (inw(iobase + IO_PORT_REG) == RCV_Done) { 722 723 rx_status = inw(iobase + IO_PORT_REG); 724 sc->rx_head = inw(iobase + IO_PORT_REG); 725 QQQ = pkt_len = inw(iobase + IO_PORT_REG); 726 727 if (rx_status & RCV_OK_bit) { 728 MGETHDR(m, M_DONTWAIT, MT_DATA); 729 ipkt = m; 730 if (ipkt == NULL) { 731 ifp->if_iqdrops++; 732 } else { 733 ipkt->m_pkthdr.rcvif = ifp; 734 ipkt->m_pkthdr.len = pkt_len; 735 ipkt->m_len = MHLEN; 736 737 while (pkt_len > 0) { 738 if (pkt_len > MINCLSIZE) { 739 MCLGET(m, M_DONTWAIT); 740 if (m->m_flags & M_EXT) { 741 m->m_len = MCLBYTES; 742 } else { 743 m_freem(ipkt); 744 ifp->if_iqdrops++; 745 goto rx_another; 746 } 747 } 748 m->m_len = min(m->m_len, pkt_len); 749 750 /* 751 * NOTE: I'm assuming that all mbufs allocated are of even length, 752 * except for the last one in an odd-length packet. 753 */ 754 755 insw(iobase + IO_PORT_REG, 756 mtod(m, caddr_t), m->m_len / 2); 757 758 if (m->m_len & 1) { 759 *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 760 } 761 pkt_len -= m->m_len; 762 763 if (pkt_len > 0) { 764 MGET(m->m_next, M_DONTWAIT, MT_DATA); 765 if (m->m_next == NULL) { 766 m_freem(ipkt); 767 ifp->if_iqdrops++; 768 goto rx_another; 769 } 770 m = m->m_next; 771 m->m_len = MLEN; 772 } 773 } 774 eh = mtod(ipkt, struct ether_header *); 775#ifdef EXDEBUG 776 if (debug_mask & Rcvd_Pkts) { 777 if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 778 printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 779 printf("%6D\n", eh->ether_dhost, ":"); 780 } /* QQQ */ 781 } 782#endif 783 (*ifp->if_input)(ifp, ipkt); 784 ifp->if_ipackets++; 785 } 786 } else { 787 ifp->if_ierrors++; 788 } 789 outw(iobase + HOST_ADDR_REG, sc->rx_head); 790rx_another: ; 791 } 792 793 if (sc->rx_head < sc->rx_lower_limit + 2) 794 outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 795 else 796 outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 797 798 DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 799 800 return; 801} 802 803 804static int 805ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 806{ 807 struct ex_softc * sc = ifp->if_softc; 808 struct ifreq * ifr = (struct ifreq *)data; 809 int s; 810 int error = 0; 811 812 DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 813 814 s = splimp(); 815 816 switch(cmd) { 817 case SIOCSIFADDR: 818 case SIOCGIFADDR: 819 case SIOCSIFMTU: 820 error = ether_ioctl(ifp, cmd, data); 821 break; 822 823 case SIOCSIFFLAGS: 824 DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 825 if ((ifp->if_flags & IFF_UP) == 0 && 826 (ifp->if_flags & IFF_RUNNING)) { 827 828 ifp->if_flags &= ~IFF_RUNNING; 829 ex_stop(sc); 830 } else { 831 ex_init(sc); 832 } 833 break; 834#ifdef NODEF 835 case SIOCGHWADDR: 836 DODEBUG(Start_End, printf("SIOCGHWADDR");); 837 bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 838 sizeof(sc->sc_addr)); 839 break; 840#endif 841 case SIOCADDMULTI: 842 case SIOCDELMULTI: 843 ex_init(sc); 844 error = 0; 845 break; 846 case SIOCSIFMEDIA: 847 case SIOCGIFMEDIA: 848 error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 849 break; 850 default: 851 DODEBUG(Start_End, printf("unknown");); 852 error = EINVAL; 853 } 854 855 splx(s); 856 857 DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 858 859 return(error); 860} 861 862static void 863ex_setmulti(struct ex_softc *sc) 864{ 865 struct ifnet *ifp; 866 struct ifmultiaddr *maddr; 867 u_int16_t *addr; 868 int iobase = sc->iobase; 869 int count; 870 int timeout, status; 871 872 ifp = &sc->arpcom.ac_if; 873 874 count = 0; 875 TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 876 if (maddr->ifma_addr->sa_family != AF_LINK) 877 continue; 878 count++; 879 } 880 881 if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) 882 || count > 63) { 883 /* Interface is in promiscuous mode or there are too many 884 * multicast addresses for the card to handle */ 885 outb(iobase + CMD_REG, Bank2_Sel); 886 outb(iobase + REG2, inb(iobase + REG2) | Promisc_Mode); 887 outb(iobase + REG3, inb(iobase + REG3)); 888 outb(iobase + CMD_REG, Bank0_Sel); 889 } 890 else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { 891 /* Program multicast addresses plus our MAC address 892 * into the filter */ 893 outb(iobase + CMD_REG, Bank2_Sel); 894 outb(iobase + REG2, inb(iobase + REG2) | Multi_IA); 895 outb(iobase + REG3, inb(iobase + REG3)); 896 outb(iobase + CMD_REG, Bank0_Sel); 897 898 /* Borrow space from TX buffer; this should be safe 899 * as this is only called from ex_init */ 900 901 outw(iobase + HOST_ADDR_REG, sc->tx_lower_limit); 902 outw(iobase + IO_PORT_REG, MC_Setup_CMD); 903 outw(iobase + IO_PORT_REG, 0); 904 outw(iobase + IO_PORT_REG, 0); 905 outw(iobase + IO_PORT_REG, (count + 1) * 6); 906 907 TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 908 if (maddr->ifma_addr->sa_family != AF_LINK) 909 continue; 910 911 addr = (u_int16_t*)LLADDR((struct sockaddr_dl *) 912 maddr->ifma_addr); 913 outw(iobase + IO_PORT_REG, *addr++); 914 outw(iobase + IO_PORT_REG, *addr++); 915 outw(iobase + IO_PORT_REG, *addr++); 916 } 917 918 /* Program our MAC address as well */ 919 /* XXX: Is this necessary? The Linux driver does this 920 * but the NetBSD driver does not */ 921 addr = (u_int16_t*)(&sc->arpcom.ac_enaddr); 922 outw(iobase + IO_PORT_REG, *addr++); 923 outw(iobase + IO_PORT_REG, *addr++); 924 outw(iobase + IO_PORT_REG, *addr++); 925 926 inw(iobase + IO_PORT_REG); 927 outw(iobase + XMT_BAR, sc->tx_lower_limit); 928 outb(iobase + CMD_REG, MC_Setup_CMD); 929 930 sc->tx_head = sc->tx_lower_limit; 931 sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6; 932 933 for (timeout=0; timeout<100; timeout++) { 934 DELAY(2); 935 if ((inb(iobase + STATUS_REG) & Exec_Int) == 0) 936 continue; 937 938 status = inb(iobase + CMD_REG); 939 outb(iobase + STATUS_REG, Exec_Int); 940 break; 941 } 942 943 sc->tx_head = sc->tx_tail; 944 } 945 else 946 { 947 /* No multicast or promiscuous mode */ 948 outb(iobase + CMD_REG, Bank2_Sel); 949 outb(iobase + REG2, inb(iobase + REG2) & 0xDE); 950 /* ~(Multi_IA | Promisc_Mode) */ 951 outb(iobase + REG3, inb(iobase + REG3)); 952 outb(iobase + CMD_REG, Bank0_Sel); 953 } 954} 955 956static void 957ex_reset(struct ex_softc *sc) 958{ 959 int s; 960 961 DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 962 963 s = splimp(); 964 965 ex_stop(sc); 966 ex_init(sc); 967 968 splx(s); 969 970 DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 971 972 return; 973} 974 975static void 976ex_watchdog(struct ifnet *ifp) 977{ 978 struct ex_softc * sc = ifp->if_softc; 979 980 DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 981 982 ifp->if_flags &= ~IFF_OACTIVE; 983 984 DODEBUG(Status, printf("OIDLE watchdog\n");); 985 986 ifp->if_oerrors++; 987 ex_reset(sc); 988 ex_start(ifp); 989 990 DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 991 992 return; 993} 994 995static int 996ex_get_media (u_int32_t iobase) 997{ 998 int current; 999 int media; 1000 1001 media = eeprom_read(iobase, EE_W5); 1002 1003 outb(iobase + CMD_REG, Bank2_Sel); 1004 current = inb(iobase + REG3); 1005 outb(iobase + CMD_REG, Bank0_Sel); 1006 1007 if ((current & TPE_bit) && (media & EE_W5_PORT_TPE)) 1008 return(IFM_ETHER|IFM_10_T); 1009 if ((current & BNC_bit) && (media & EE_W5_PORT_BNC)) 1010 return(IFM_ETHER|IFM_10_2); 1011 1012 if (media & EE_W5_PORT_AUI) 1013 return (IFM_ETHER|IFM_10_5); 1014 1015 return (IFM_ETHER|IFM_AUTO); 1016} 1017 1018static int 1019ex_ifmedia_upd (ifp) 1020 struct ifnet * ifp; 1021{ 1022 struct ex_softc * sc = ifp->if_softc; 1023 1024 if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER) 1025 return EINVAL; 1026 1027 return (0); 1028} 1029 1030static void 1031ex_ifmedia_sts(ifp, ifmr) 1032 struct ifnet * ifp; 1033 struct ifmediareq * ifmr; 1034{ 1035 struct ex_softc * sc = ifp->if_softc; 1036 1037 ifmr->ifm_active = ex_get_media(sc->iobase); 1038 ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 1039 1040 return; 1041} 1042 1043u_short 1044eeprom_read(u_int32_t iobase, int location) 1045{ 1046 int i; 1047 u_short data = 0; 1048 int ee_addr; 1049 int read_cmd = location | EE_READ_CMD; 1050 short ctrl_val = EECS; 1051 1052 ee_addr = iobase + EEPROM_REG; 1053 outb(iobase + CMD_REG, Bank2_Sel); 1054 outb(ee_addr, EECS); 1055 for (i = 8; i >= 0; i--) { 1056 short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 1057 outb(ee_addr, outval); 1058 outb(ee_addr, outval | EESK); 1059 DELAY(3); 1060 outb(ee_addr, outval); 1061 DELAY(2); 1062 } 1063 outb(ee_addr, ctrl_val); 1064 1065 for (i = 16; i > 0; i--) { 1066 outb(ee_addr, ctrl_val | EESK); 1067 DELAY(3); 1068 data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 1069 outb(ee_addr, ctrl_val); 1070 DELAY(2); 1071 } 1072 1073 ctrl_val &= ~EECS; 1074 outb(ee_addr, ctrl_val | EESK); 1075 DELAY(3); 1076 outb(ee_addr, ctrl_val); 1077 DELAY(2); 1078 outb(iobase + CMD_REG, Bank0_Sel); 1079 return(data); 1080} 1081