/* atlantic.c: Setup program for AT/LANTIC DP83905 ethercards. */ /* This is a setup and diagnostic program for ISA NE2000 Ethernet adapters. It has specific support for configuring the National Semiconductor AT/LANTIC DP83905 used in on ISA Ethernet adapters such as the NE2000plus. It also works several work-alike chips from other vendors. This program must be compiled with '-O'. If you have unresolved references to e.g. inw(), then you haven't used -O. The suggested compile command is at the bottom of this file. Copyright 1994-2000 by Donald Becker. This software may be used and distributed according to the terms of the GNU General Public License (GPL), incorporated herein by reference. Contact the author for use under other terms. The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 Annapolis MD 21403 Support and updates are available at http://www.scyld.com/diag/index.html Common-sense licensing statement: Using any portion of this program in your own program means that you must give credit to the original author and release the resulting code under the GPL. You must include the text of the license with any redistribution. See http://www.gnu.org/copyleft/gpl.txt */ #if 0 static char vcid[] = "$Id: atlantic.c,v 1.7 2000/07/24 04:58:41 becker Exp $"; #endif static char *version_msg = "atlantic.c v1.01 7/23/00 Donald Becker http://www.scyld.com/diag/index.html\n"; static char *usage_msg = "Usage: atlantic [-afhNsvVwW] [-p ] [-F ] [-P ] -p Use the card at this I/O address (default 0x300). EEPROM configuration commands take effect at the next reset -F 10baseT, 10base2, AUI, 10baset Set the specified transceiver type. -Q 3, 4, 5, 9, 10, 11, 12, 15 Set the IRQ line. -P 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360 New I/O base. -N or -W Set to NE2000 or WD8013 clone mode (mutually exclusive) "; #if ! defined(__OPTIMIZE__) #warning You must compile this program with the correct options! #error You must compile this driver with "-O". #endif #include /* Hey, we're all POSIX here, right? */ #include #include #include #include #include #if defined(__linux__) && __GNU_LIBRARY__ == 1 #include /* Newer libraries use instead. */ #else #include #endif /* Two functions that some GLIBC header file sets omit. grrr... */ extern int ioperm(unsigned long __base, unsigned long extent, int on_or_off); extern int iopl(int level); struct option longopts[] = { /* { name has_arg *flag val } */ {"base-address", 1, 0, 'p'}, /* Base I/O port address. */ {"help", 0, 0, 'h'}, /* Give help */ {"interface", 0, 0, 'F'}, /* Set the transceiver type (built-in, AUI) */ {"NE2000", 1, 0, 'N'}, /* Set to NE2000 mode */ {"new_ioport", 1, 0, 'P'}, /* Set a I/O location for the card. */ {"irq", 1, 0, 'Q'}, /* Set a new IRQ (interrupt) line. */ {"wd8013", 1, 0, 'W'}, /* Set to WD8013 mode */ {"verbose", 0, 0, 'v'}, /* Verbose mode */ {"version", 0, 0, 'V'}, /* Display version number */ {"write-EEPROM", 1, 0, 'w'},/* Write th EEPROMS with the specified vals */ { 0, 0, 0, 0 } }; /* Three configuration registers are read and may be set from the EEPROM. They are named registers A, B and C RegA NE/WD FREAD INT2 INT1 INT0 IOAD2 IOAD1 IOAD0 RegB EELOAD BTPR BUSE CHRDY IO16 GDLINK XCVR1 XCVR0 RegC SoftEn ClkSel IntMde COMP BtPR3 BtPR2 BtPR1 BtPROM0 */ /* I/O base settings, IOAD2-0, in register A. */ static int io_base[8] = { 0x300, 0x278, 0x240, 0x280, 0x2C0, 0x320, 0x340, 0x360}; /* The IRQ line settings, INT2-0, in register A, and a reverse map. */ static int index2irq[8] = {3, 4, 5, 9, 10, 11, 12, 15}; static int irq2index[16] = {-1, -1, 3, 0, 1, 2, -1, -1, -1, 3, 4, 5, 6, -1, -1, 7}; /* The transceiver settings, XCVR1-0, in register B. 10baseT-LRT is Low Receive Threshold -- "turning the volume up" for long wiring runs. */ static char *xcvr_name[4] = {"10baseT", "Thinnet", "AUI", "10baseT (LRT)"}; int opt_f = 0; static void show_config(int regA, int regB); static void write_EEPROM(int dp8390_base, int regA, int regB, int regC); /* I should say something here... uuhhhhmmm.... */ int main(int argc, char *argv[]) { extern char *optarg; int port_base = 0x300; /* Base address of the board. */ int ioaddr = 0x300; /* Base address of the 8390 registers. */ int new_ioport = -1, new_irq = -1, new_mode = 0; int errflag = 0, verbose = 0, wd_mode = 0, write_eeprom = 0; int show_version = 0, opt_a = 0; int new_io_idx = -1, xcvr = -1; /* I/O port, Transceiver type to set. */ int regA, regB, old_regA, old_regB; int option; while ((option = getopt(argc, argv, "afF:hi:Q:m:Np:P:vVwW")) != -1) switch (option) { case 'a': opt_a++; break; case 'f': opt_f++; break; case 'F': if (strncmp(optarg, "10base", 6) == 0) { switch (optarg[6]) { case 'T': xcvr = 0; break; case '2': xcvr = 1; break; case '5': xcvr = 2; break; default: errflag++; } } else if (strcmp(optarg, "AUI") == 0) xcvr = 2; else if (optarg[0] >= '0' && optarg[0] <= '3' && optarg[1] == 0) xcvr = optarg[0] - '0'; else { fprintf(stderr, "Invalid interface specified: it must be" " 0..3, '10base{T,2,5}' or 'AUI'.\n"); errflag++; } break; case 'h': printf(usage_msg); return 0; case 'N': wd_mode = 0; new_mode = 2; break; case 'i': case 'Q': new_irq = atoi(optarg); if (new_irq < 3 || new_irq > 15 || new_irq == 6 || new_irq == 8) { fprintf(stderr, "Invalid new IRQ %#x. Valid values: " "3-5,7,9-15.\n", new_irq); errflag++; } break; case 'p': port_base = strtol(optarg, NULL, 16); break; case 'P': new_ioport = strtol(optarg, NULL, 16); break; case 'v': verbose++; break; case 'V': show_version++; break; case 'w': write_eeprom++; break; case 'W': wd_mode++; new_mode = 1; break; case '?': errflag++; } if (errflag) { fprintf(stderr, usage_msg); return 3; } if (verbose || show_version) printf(version_msg); /* Turn on access to the I/O ports. */ if (ioperm(port_base, 32, 1) < 0) { perror("atlantic: io-perm"); fprintf(stderr, " (You must run this program with 'root' permissions.)\n"); return 2; } /* Check the specified new I/O port value. */ if (new_irq >= 0 && (new_irq >= 16 || irq2index[new_irq] < 0)) { fprintf(stderr, "The new IRQ line, %d, is invalid.\n", new_irq); return 3; } if (new_ioport >= 0) { int i; for (i = 0; i < 8; i++) if (new_ioport == io_base[i]) break; if (i >= 8) { fprintf(stderr, "The new base I/O address, %#x, is invalid.\n", new_ioport); return 3; } new_io_idx = i; } if ( ! opt_f && inb(port_base) == 0xff) { printf("No AT/LANTIC chip found at I/O %#x.\n" " Use '-f' to override if you are certain the chip is at this" " I/O location.\n", port_base); return 1; } /* First find if this card is set to WD or NE mode by locating the 8390 registers. */ { int saved_cmd = inb(port_base); int cntr; outb(0x20, port_base); inb(port_base + 13); /* Clear a counter register */ cntr = inb(port_base + 13); if (verbose) printf(" The NE2K 8390 cmd register was %2.2x, cntr %d.\n", saved_cmd, cntr); if (cntr) ioaddr = port_base + 16; else ioaddr = port_base; outb(saved_cmd, port_base); } if (opt_a) { int saved_0 = inb(port_base); int window, i; printf("8390 registers at %#x (command register was %2.2x)\n", ioaddr, saved_0); for (window = 0; window < 4; window++) { printf(" Window %d:", window); outb((window<<6) | 0x20, ioaddr); for(i = 0; i < 16; i++) { if (window == 0 && i == 6) printf(" **"); else printf(" %2.2x", inb(ioaddr + i)); } printf(".\n"); } if (ioaddr != port_base) { /* WD emulation. */ printf("WD8013 compatible registers: "); for(i = 0; i < 16; i++) { printf(" %2.2x", inb(port_base + i)); fflush(stdout); } printf(".\n"); } } printf("Reading the configuration from the AT/LANTIC at %#3x...\n", port_base); outb(0x21, ioaddr); /* Select Window 0 */ old_regA = regA = inb(ioaddr + 0x0A); old_regB = regB = inb(ioaddr + 0x0B); printf("The current configuration is\n"); show_config(regA, regB); /* This should never be triggered. We see the card after all!*/ if ((regA & 7) == 1) printf(" Your card is set to be configured at boot-time.\n" " Remaining with this software configuration is a bad idea" " when using\n modern systems. The card will likely be" " unintentionally activated at\n unexpected I/O and IRQ" " settings.\n" ); if (new_mode) regA = (regA & ~0x80) | (new_mode == 1 ? 0x80 : 0); if (new_irq >= 0) regA = (regA & ~0x38) | (irq2index[new_irq]<<3); if (new_io_idx >= 0) regA = (regA & ~0x07) | new_io_idx; if (xcvr >= 0) regB = (regB & 0x5C) | (xcvr & 0x3); if (regA != old_regA || regB != old_regB || write_eeprom) { printf("The proposed new configuration is\n"); show_config(regA, regB); if ( ! write_eeprom) printf(" Use '-w' to set this as the current configuration " "(valid until next reset).\n" " Use '-w -w' to write the settings to the EEPROM.\n" " Use '-w -w -w' to do both.\n"); } /* We must write the EEPROM and register B before possibly moving the I/O base. */ if (write_eeprom > 1) { printf("Writing the new configuration to the EEPROM...\n"); write_EEPROM(ioaddr, regA, regB, 0x30); printf("Wrote the EEPROM at %#3x with 0x%2.2x 0x%2.2x 0x%2.2x.\n", port_base, regA, regB, 0x30); } if (write_eeprom & 1) { if (regB != old_regB) { printf(" Setting register B to %#02x.\n", regB); inb(ioaddr + 0x0B); outb(regB, ioaddr + 0x0B); regB = inb(ioaddr + 0x0B); printf(" Register B is now %#02x: Interface %s.\n", regB, xcvr_name[regB & 0x03]); } if (regA != old_regA) { printf(" Setting register A to %#02x (%s mode I/O %#x IRQ %d).\n", regA, regA & 0x80 ? "WD8013" : "NE2000", io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07] ); inb(ioaddr + 0x0A); outb(regA, ioaddr + 0x0A); if (port_base == io_base[regA&7]) { ioaddr = port_base + (regA & 0x80 ? 16 : 0); printf(" Register A at base %x is now %#02x.\n", ioaddr, inb(ioaddr + 0x0A)); } } } return 0; } static void show_config(int regA, int regB) { printf(" Register A 0x%2.2x: I/O base @ %#x, IRQ %d," " %s mode, %s ISA read.\n", regA, io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07], regA & 0x80 ? "WD8013" : "NE2000", regA & 0x40 ? "fast" : "normal" ); printf(" Register B 0x%2.2x: Interface %s.\n", regB, xcvr_name[regB & 0x03]); printf(" Boot PROM writes are %sabled, CHRDY after %s%s.\n", regB & 0x40 ? "en" : "dis", regB & 0x10 ? "BALE" : "IORD/IOWR", regB & 0x08 ? ", IO16 after IORD/IOWR" : ""); if (regB & 0x20) printf(" Bus error detected.\n"); } /* Write the A, B, and C configuration registers into the EEPROM at DP8390_BASE. Ideally this would be done with interrupts disabled, but we do not have that capability with user level code. */ static void write_EEPROM(int dp8390_base, int regA, int regB, int regC) { int ioregb = dp8390_base + 0x0B; int currB = inb(ioregb) & (~0x04) ; outb(currB | 0x80, ioregb); inb(ioregb); outb(regA, ioregb); outb(regB, ioregb); outb(regC, ioregb); return; } /* * Local variables: * compile-command: "gcc -Wall -Wstrict-prototypes -O6 -o atlantic atlantic.c" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */