|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1994 Shantanu Goel ! 3: * All Rights Reserved. ! 4: * ! 5: * Permission to use, copy, modify and distribute this software and its ! 6: * documentation is hereby granted, provided that both the copyright ! 7: * notice and this permission notice appear in all copies of the ! 8: * software, derivative works or modified versions, and any portions ! 9: * thereof, and that both notices appear in supporting documentation. ! 10: * ! 11: * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 12: * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 13: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 14: */ ! 15: ! 16: /* ! 17: * Written 1993 by Donald Becker. ! 18: * ! 19: * Copyright 1993 United States Government as represented by the ! 20: * Director, National Security Agency. This software may be used and ! 21: * distributed according to the terms of the GNU Public License, ! 22: * incorporated herein by reference. ! 23: * ! 24: * The Author may be reached as [email protected] or ! 25: * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 ! 26: */ ! 27: ! 28: #include <wd.h> ! 29: #if NWD > 0 ! 30: /* ! 31: * Driver for SMC/Western Digital Ethernet adaptors. ! 32: * Derived from the Linux driver by Donald Becker. ! 33: * ! 34: * Shantanu Goel ([email protected]) ! 35: */ ! 36: #include <mach/sa/sys/types.h> ! 37: #include "vm_param.h" ! 38: #include <kern/time_out.h> ! 39: #include <device/device_types.h> ! 40: #include <device/errno.h> ! 41: #include <device/io_req.h> ! 42: #include <device/if_hdr.h> ! 43: #include <device/if_ether.h> ! 44: #include <device/net_status.h> ! 45: #include <device/net_io.h> ! 46: #include <chips/busses.h> ! 47: #include <i386/machspl.h> ! 48: #include <i386/pio.h> ! 49: #include <i386at/gpl/if_nsreg.h> ! 50: ! 51: #define WD_START_PG 0x00 /* first page of TX buffer */ ! 52: #define WD03_STOP_PG 0x20 /* last page +1 of RX ring */ ! 53: #define WD13_STOP_PG 0x40 /* last page +1 of RX ring */ ! 54: ! 55: #define WD_CMDREG 0 /* offset of ASIC command register */ ! 56: #define WD_RESET 0x80 /* board reset in WDTRA_CMDREG */ ! 57: #define WD_MEMEN 0x40 /* enable shared memory */ ! 58: #define WD_CMDREG5 5 /* offset of 16-bit-only ASIC register 5 */ ! 59: #define ISA16 0x80 /* enable 16 bit access from the ISA bus */ ! 60: #define NIC16 0x40 /* enable 16 bit access from the 8390 */ ! 61: #define WD_NIC_OFF 16 /* NIC register offset */ ! 62: ! 63: #define wdunit(dev) minor(dev) ! 64: ! 65: /* ! 66: * Autoconfiguration stuff. ! 67: */ ! 68: int wdprobe(); ! 69: void wdattach(); ! 70: int wdstd[] = { 0x300, 0x280, 0x380, 0x240, 0 }; ! 71: struct bus_device *wdinfo[NWD]; ! 72: struct bus_driver wddriver = { ! 73: wdprobe, 0, wdattach, 0, wdstd, "wd", wdinfo, 0, 0, 0 ! 74: }; ! 75: ! 76: /* ! 77: * NS8390 state. ! 78: */ ! 79: struct nssoftc wdnssoftc[NWD]; ! 80: ! 81: /* ! 82: * Board state. ! 83: */ ! 84: struct wdsoftc { ! 85: int sc_mstart; /* start of board's RAM */ ! 86: int sc_mend; /* end of board's RAM */ ! 87: int sc_rmstart; /* start of receive RAM */ ! 88: int sc_rmend; /* end of receive RAM */ ! 89: int sc_reg0; /* copy of register 0 of ASIC */ ! 90: int sc_reg5; /* copy of register 5 of ASIC */ ! 91: } wdsoftc[NWD]; ! 92: ! 93: void wdstart(int); ! 94: void wd_reset(struct nssoftc *sc); ! 95: void wd_input(struct nssoftc *sc, int, char *, int); ! 96: int wd_output(struct nssoftc *sc, int, char *, int); ! 97: ! 98: /* ! 99: * Watchdog. ! 100: */ ! 101: int wdwstart = 0; ! 102: void wdwatch(void); ! 103: ! 104: #define WDDEBUG ! 105: #ifdef WDDEBUG ! 106: int wddebug = 0; ! 107: #define DEBUGF(stmt) { if (wddebug) stmt; } ! 108: #else ! 109: #define DEBUGF(stmt) ! 110: #endif ! 111: ! 112: /* ! 113: * Probe for the WD8003 and WD8013. ! 114: * These cards have the station address PROM at I/O ports <base>+8 ! 115: * to <base>+13, with a checksum following. A Soundblaster can have ! 116: * the same checksum as a WD ethercard, so we have an extra exclusionary ! 117: * check for it. ! 118: */ ! 119: int ! 120: wdprobe(xxx, ui) ! 121: int xxx; ! 122: struct bus_device *ui; ! 123: { ! 124: int *port; ! 125: ! 126: if (ui->unit >= NWD) { ! 127: printf("wd%d: not configured\n", ui->unit); ! 128: return (0); ! 129: } ! 130: for (port = wdstd; *port; port++) { ! 131: if (*port < 0) ! 132: continue; ! 133: if (inb(*port + 8) != 0xff ! 134: && inb(*port + 9) != 0xff ! 135: && wdprobe1(*port, ui)) { ! 136: ui->address = *port; ! 137: *port = -1; ! 138: return (1); ! 139: } ! 140: } ! 141: return (0); ! 142: } ! 143: ! 144: int ! 145: wdprobe1(port, ui) ! 146: int port; ! 147: struct bus_device *ui; ! 148: { ! 149: ! 150: int i, irq = 0, checksum = 0, ancient = 0, word16 = 0; ! 151: struct wdsoftc *wd = &wdsoftc[ui->unit]; ! 152: struct nssoftc *ns = &wdnssoftc[ui->unit]; ! 153: struct ifnet *ifp = &ns->sc_if; ! 154: ! 155: for (i = 0; i < 8; i++) ! 156: checksum += inb(port + 8 + i); ! 157: if ((checksum & 0xff) != 0xff) ! 158: return (0); ! 159: ! 160: printf("wd%d: WD80x3 at 0x%03x, ", ui->unit, port); ! 161: for (i = 0; i < ETHER_ADDR_LEN; i++) { ! 162: if (i == 0) ! 163: printf("%02x", ns->sc_addr[i] = inb(port + 8 + i)); ! 164: else ! 165: printf(":%02x", ns->sc_addr[i] = inb(port + 8 + i)); ! 166: } ! 167: /* ! 168: * Check for PureData. ! 169: */ ! 170: if (inb(port) == 'P' && inb(port + 1) == 'D') { ! 171: u_char reg5 = inb(port + 5); ! 172: ! 173: switch (inb(port + 2)) { ! 174: ! 175: case 0x03: ! 176: case 0x05: ! 177: word16 = 0; ! 178: break; ! 179: ! 180: case 0x0a: ! 181: word16 = 1; ! 182: break; ! 183: ! 184: default: ! 185: word16 = 0; ! 186: break; ! 187: } ! 188: wd->sc_mstart = ((reg5 & 0x1c) + 0xc0) << 12; ! 189: irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; ! 190: } else { ! 191: /* ! 192: * Check for 8 bit vs 16 bit card. ! 193: */ ! 194: for (i = 0; i < ETHER_ADDR_LEN; i++) ! 195: if (inb(port + i) != inb(port + 8 + i)) ! 196: break; ! 197: if (i >= ETHER_ADDR_LEN) { ! 198: ancient = 1; ! 199: word16 = 0; ! 200: } else { ! 201: int tmp = inb(port + 1); ! 202: ! 203: /* ! 204: * Attempt to clear 16bit bit. ! 205: */ ! 206: outb(port + 1, tmp ^ 0x01); ! 207: if (((inb(port + 1) & 0x01) == 0x01) /* 16 bit */ ! 208: && (tmp & 0x01) == 0x01) { /* in 16 bit slot */ ! 209: int asic_reg5 = inb(port + WD_CMDREG5); ! 210: ! 211: /* ! 212: * Magic to set ASIC to word-wide mode. ! 213: */ ! 214: outb(port+WD_CMDREG5, NIC16|(asic_reg5&0x1f)); ! 215: outb(port + 1, tmp); ! 216: word16 = 1; ! 217: } else ! 218: word16 = 0; ! 219: outb(port + 1, tmp); ! 220: } ! 221: if (!ancient && (inb(port + 1) & 0x01) != (word16 & 0x01)) ! 222: printf("\nwd%d: bus width conflict, " ! 223: "%d (probe) != %d (reg report)", ui->unit, ! 224: word16 ? 16 : 8, (inb(port+1) & 0x01) ? 16 : 8); ! 225: } ! 226: /* ! 227: * Determine board's RAM location. ! 228: */ ! 229: if (wd->sc_mstart == 0) { ! 230: int reg0 = inb(port); ! 231: ! 232: if (reg0 == 0xff || reg0 == 0) ! 233: wd->sc_mstart = 0xd0000; ! 234: else { ! 235: int high_addr_bits = inb(port + WD_CMDREG5) & 0x1f; ! 236: ! 237: if (high_addr_bits == 0x1f || word16 == 0) ! 238: high_addr_bits = 0x01; ! 239: wd->sc_mstart = ((reg0&0x3f)<<13)+(high_addr_bits<<19); ! 240: } ! 241: } ! 242: /* ! 243: * Determine irq. ! 244: */ ! 245: if (irq == 0) { ! 246: int irqmap[] = { 9, 3, 5, 7, 10, 11, 15, 4 }; ! 247: int reg1 = inb(port + 1); ! 248: int reg4 = inb(port + 4); ! 249: ! 250: /* ! 251: * For old card, irq must be supplied. ! 252: */ ! 253: if (ancient || reg1 == 0xff) { ! 254: if (ui->sysdep1 == 0) { ! 255: printf("\nwd%d: must specify IRQ for card\n", ! 256: ui->unit); ! 257: return (0); ! 258: } ! 259: irq = ui->sysdep1; ! 260: } else { ! 261: DEBUGF({ ! 262: int i = ((reg4 >> 5) & 0x03) + (reg1 & 0x04); ! 263: ! 264: printf("\nwd%d: irq index %d\n", ui->unit, i); ! 265: printf("wd%d:", ui->unit); ! 266: }) ! 267: irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; ! 268: } ! 269: } else if (irq == 2) ! 270: irq = 9; ! 271: ui->sysdep1 = irq; ! 272: take_dev_irq(ui); ! 273: printf(", irq %d", irq); ! 274: ! 275: /* ! 276: * Initialize 8390 state. ! 277: */ ! 278: ns->sc_name = ui->name; ! 279: ns->sc_unit = ui->unit; ! 280: ns->sc_port = port + WD_NIC_OFF; ! 281: ns->sc_reset = wd_reset; ! 282: ns->sc_input = wd_input; ! 283: ns->sc_output = wd_output; ! 284: ns->sc_pingpong = 1; ! 285: ns->sc_word16 = word16; ! 286: ns->sc_txstrtpg = WD_START_PG; ! 287: ns->sc_rxstrtpg = WD_START_PG + TX_PAGES(ns); ! 288: ns->sc_stoppg = word16 ? WD13_STOP_PG : WD03_STOP_PG; ! 289: ! 290: wd->sc_rmstart = wd->sc_mstart + TX_PAGES(ns) * 256; ! 291: wd->sc_mend = wd->sc_rmend ! 292: = wd->sc_mstart + (ns->sc_stoppg - WD_START_PG) * 256; ! 293: printf(", memory 0x%05x-0x%05x", wd->sc_mstart, wd->sc_mend); ! 294: ! 295: if (word16) ! 296: printf(", 16 bit"); ! 297: printf("\n"); ! 298: ! 299: DEBUGF(printf("wd%d: txstrtpg %d rxstrtpg %d num_pages %d\n", ! 300: ui->unit, ns->sc_txstrtpg, ns->sc_rxstrtpg, ! 301: (wd->sc_mend - wd->sc_mstart) / 256)); ! 302: ! 303: /* ! 304: * Initialize interface header. ! 305: */ ! 306: ifp->if_unit = ui->unit; ! 307: ifp->if_mtu = ETHERMTU; ! 308: ifp->if_flags = IFF_BROADCAST; ! 309: ifp->if_header_size = sizeof(struct ether_header); ! 310: ifp->if_header_format = HDR_ETHERNET; ! 311: ifp->if_address_size = ETHER_ADDR_LEN; ! 312: ifp->if_address = ns->sc_addr; ! 313: if_init_queues(ifp); ! 314: ! 315: return (1); ! 316: } ! 317: ! 318: void ! 319: wdattach(ui) ! 320: struct bus_device *ui; ! 321: { ! 322: /* ! 323: * void ! 324: */ ! 325: } ! 326: ! 327: int ! 328: wdopen(dev, flag) ! 329: dev_t dev; ! 330: int flag; ! 331: { ! 332: int unit = wdunit(dev), s; ! 333: struct bus_device *ui; ! 334: struct wdsoftc *wd; ! 335: struct nssoftc *ns; ! 336: ! 337: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0) ! 338: return (ENXIO); ! 339: ! 340: /* ! 341: * Start watchdog. ! 342: */ ! 343: if (!wdwstart) { ! 344: wdwstart++; ! 345: timeout(wdwatch, 0, hz); ! 346: } ! 347: wd = &wdsoftc[unit]; ! 348: ns = &wdnssoftc[unit]; ! 349: ns->sc_if.if_flags |= IFF_UP; ! 350: s = splimp(); ! 351: wd->sc_reg0 = ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN; ! 352: wd->sc_reg5 = ((wd->sc_mstart >> 19) & 0x1f) | NIC16; ! 353: if (ns->sc_word16) ! 354: outb(ui->address + WD_CMDREG5, wd->sc_reg5); ! 355: outb(ui->address, wd->sc_reg0); ! 356: nsinit(ns); ! 357: splx(s); ! 358: return (0); ! 359: } ! 360: ! 361: int ! 362: wdoutput(dev, ior) ! 363: dev_t dev; ! 364: io_req_t ior; ! 365: { ! 366: int unit = wdunit(dev); ! 367: struct bus_device *ui; ! 368: ! 369: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0) ! 370: return (ENXIO); ! 371: ! 372: return (net_write(&wdnssoftc[unit].sc_if, wdstart, ior)); ! 373: } ! 374: ! 375: int ! 376: wdsetinput(dev, receive_port, priority, filter, filter_count) ! 377: dev_t dev; ! 378: mach_port_t receive_port; ! 379: int priority; ! 380: filter_t *filter; ! 381: unsigned filter_count; ! 382: { ! 383: int unit = wdunit(dev); ! 384: struct bus_device *ui; ! 385: ! 386: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0) ! 387: return (ENXIO); ! 388: ! 389: return (net_set_filter(&wdnssoftc[unit].sc_if, receive_port, ! 390: priority, filter, filter_count)); ! 391: } ! 392: ! 393: int ! 394: wdgetstat(dev, flavor, status, count) ! 395: dev_t dev; ! 396: int flavor; ! 397: dev_status_t status; ! 398: unsigned *count; ! 399: { ! 400: int unit = wdunit(dev); ! 401: struct bus_device *ui; ! 402: ! 403: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0) ! 404: return (ENXIO); ! 405: ! 406: return (net_getstat(&wdnssoftc[unit].sc_if, flavor, status, count)); ! 407: } ! 408: ! 409: int ! 410: wdsetstat(dev, flavor, status, count) ! 411: dev_t dev; ! 412: int flavor; ! 413: dev_status_t status; ! 414: unsigned count; ! 415: { ! 416: int unit = wdunit(dev), oflags, s; ! 417: struct bus_device *ui; ! 418: struct ifnet *ifp; ! 419: struct net_status *ns; ! 420: ! 421: if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0) ! 422: return (ENXIO); ! 423: ! 424: ifp = &wdnssoftc[unit].sc_if; ! 425: ! 426: switch (flavor) { ! 427: ! 428: case NET_STATUS: ! 429: if (count < NET_STATUS_COUNT) ! 430: return (D_INVALID_SIZE); ! 431: ns = (struct net_status *)status; ! 432: oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC); ! 433: ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC); ! 434: ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC); ! 435: if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) { ! 436: s = splimp(); ! 437: nsinit(&wdnssoftc[unit]); ! 438: splx(s); ! 439: } ! 440: break; ! 441: ! 442: default: ! 443: return (D_INVALID_OPERATION); ! 444: } ! 445: return (D_SUCCESS); ! 446: } ! 447: ! 448: void ! 449: wdintr(unit) ! 450: int unit; ! 451: { ! 452: nsintr(&wdnssoftc[unit]); ! 453: } ! 454: ! 455: void ! 456: wdstart(unit) ! 457: int unit; ! 458: { ! 459: nsstart(&wdnssoftc[unit]); ! 460: } ! 461: ! 462: void ! 463: wd_reset(ns) ! 464: struct nssoftc *ns; ! 465: { ! 466: int port = ns->sc_port - WD_NIC_OFF; /* ASIC base address */ ! 467: struct wdsoftc *wd = &wdsoftc[ns->sc_unit]; ! 468: ! 469: outb(port, WD_RESET); ! 470: outb(0x80, 0); /* I/O delay */ ! 471: /* ! 472: * Set up the ASIC registers, just in case something changed them. ! 473: */ ! 474: outb(port, ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN); ! 475: if (ns->sc_word16) ! 476: outb(port + WD_CMDREG5, NIC16 | ((wd->sc_mstart>>19) & 0x1f)); ! 477: } ! 478: ! 479: void ! 480: wd_input(ns, count, buf, ring_offset) ! 481: struct nssoftc *ns; ! 482: int count; ! 483: char *buf; ! 484: int ring_offset; ! 485: { ! 486: int port = ns->sc_port - WD_NIC_OFF; ! 487: int xfer_start; ! 488: struct wdsoftc *wd = &wdsoftc[ns->sc_unit]; ! 489: ! 490: DEBUGF(printf("wd%d: ring_offset = %d\n", ns->sc_unit, ring_offset)); ! 491: ! 492: xfer_start = wd->sc_mstart + ring_offset - (WD_START_PG << 8); ! 493: ! 494: /* ! 495: * The NIC driver calls us 3 times. Once to read the NIC 4 byte ! 496: * header, next to read the Ethernet header and finally to read ! 497: * the actual data. We enable 16 bit mode before the NIC header ! 498: * and disable it after the packet body. ! 499: */ ! 500: if (count == 4) { ! 501: if (ns->sc_word16) ! 502: outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5); ! 503: ((int *)buf)[0] = ((int *)phystokv(xfer_start))[0]; ! 504: return; ! 505: } ! 506: if (count == sizeof(struct ether_header)) { ! 507: xfer_start = (int)phystokv(xfer_start); ! 508: ((int *)buf)[0] = ((int *)xfer_start)[0]; ! 509: ((int *)buf)[1] = ((int *)xfer_start)[1]; ! 510: ((int *)buf)[2] = ((int *)xfer_start)[2]; ! 511: ((short *)(buf + 12))[0] = ((short *)(xfer_start + 12))[0]; ! 512: return; ! 513: } ! 514: if (xfer_start + count > wd->sc_rmend) { ! 515: int semi_count = wd->sc_rmend - xfer_start; ! 516: ! 517: /* ! 518: * Input move must be wrapped. ! 519: */ ! 520: bcopy((char *)phystokv(xfer_start), buf, semi_count); ! 521: count -= semi_count; ! 522: bcopy((char *)phystokv(wd->sc_rmstart),buf+semi_count,count); ! 523: } else ! 524: bcopy((char *)phystokv(xfer_start), buf, count); ! 525: if (ns->sc_word16) ! 526: outb(port + WD_CMDREG5, wd->sc_reg5); ! 527: } ! 528: ! 529: int ! 530: wd_output(ns, count, buf, start_page) ! 531: struct nssoftc *ns; ! 532: int count; ! 533: char *buf; ! 534: int start_page; ! 535: { ! 536: char *shmem; ! 537: int i, port = ns->sc_port - WD_NIC_OFF; ! 538: struct wdsoftc *wd = &wdsoftc[ns->sc_unit]; ! 539: ! 540: DEBUGF(printf("wd%d: start_page = %d\n", ns->sc_unit, start_page)); ! 541: ! 542: shmem = (char *)phystokv(wd->sc_mstart+((start_page-WD_START_PG)<<8)); ! 543: if (ns->sc_word16) { ! 544: outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5); ! 545: bcopy(buf, shmem, count); ! 546: outb(port + WD_CMDREG5, wd->sc_reg5); ! 547: } else ! 548: bcopy(buf, shmem, count); ! 549: while (count < ETHERMIN + sizeof(struct ether_header)) { ! 550: *(shmem + count) = 0; ! 551: count++; ! 552: } ! 553: return (count); ! 554: } ! 555: ! 556: /* ! 557: * Watchdog. ! 558: * Check for hung transmissions. ! 559: */ ! 560: void ! 561: wdwatch() ! 562: { ! 563: int unit, s; ! 564: struct nssoftc *ns; ! 565: ! 566: timeout(wdwatch, 0, hz); ! 567: ! 568: s = splimp(); ! 569: for (unit = 0; unit < NWD; unit++) { ! 570: if (wdinfo[unit] == 0 || wdinfo[unit]->alive == 0) ! 571: continue; ! 572: ns = &wdnssoftc[unit]; ! 573: if (ns->sc_timer && --ns->sc_timer == 0) { ! 574: printf("wd%d: transmission timeout\n", unit); ! 575: nsinit(ns); ! 576: } ! 577: } ! 578: splx(s); ! 579: } ! 580: ! 581: #endif /* NWD > 0 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.