]> git.pld-linux.org Git - packages/atlantic.git/blob - atlantic.c
6b2dc18723acf47620e60d1adcd5cebf82209b94
[packages/atlantic.git] / atlantic.c
1 /* atlantic.c: Setup program for AT/LANTIC DP83905 ethercards. */
2 /*
3         This is a setup and diagnostic program for ISA NE2000 Ethernet adapters.
4         It has specific support for configuring the National Semiconductor
5         AT/LANTIC DP83905 used in on ISA Ethernet adapters such as the NE2000plus.
6         It also works several work-alike chips from other vendors.
7
8         This program must be compiled with '-O'.
9         If you have unresolved references to e.g. inw(), then you haven't used -O.
10         The suggested compile command is at the bottom of this file.
11
12         Copyright 1994-2000 by Donald Becker.
13         This software may be used and distributed according to the terms of
14         the GNU General Public License (GPL), incorporated herein by reference.
15         Contact the author for use under other terms.
16
17         The author may be reached as becker@scyld.com, or C/O
18          Scyld Computing Corporation
19          410 Severn Ave., Suite 210
20          Annapolis MD 21403
21
22         Support and updates are available at
23         http://www.scyld.com/diag/index.html
24
25         Common-sense licensing statement: Using any portion of this program in
26         your own program means that you must give credit to the original author
27         and release the resulting code under the GPL.  You must include the
28         text of the license with any redistribution.  See
29          http://www.gnu.org/copyleft/gpl.txt
30 */
31 #if 0
32 static char vcid[] = "$Id$";
33 #endif
34
35 static char *version_msg =
36 "atlantic.c v1.01 7/23/00 Donald Becker http://www.scyld.com/diag/index.html\n";
37 static char *usage_msg =
38 "Usage: atlantic [-afhNsvVwW] [-p <IOport>] [-F <xcvr-type>] [-P <newIOport>]
39    -p <I/O base address>        Use the card at this I/O address (default 0x300).
40   EEPROM configuration commands take effect at the next reset
41    -F 10baseT, 10base2, AUI, 10baset   Set the specified transceiver type.
42    -Q 3, 4, 5, 9, 10, 11, 12, 15       Set the IRQ line.
43    -P 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360  New I/O base.
44    -N or -W         Set to NE2000 or WD8013 clone mode (mutually exclusive)
45 ";
46
47 #if ! defined(__OPTIMIZE__)
48 #warning  You must compile this program with the correct options!
49 #error You must compile this driver with "-O".
50 #endif
51 #include <unistd.h>                             /* Hey, we're all POSIX here, right? */
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <getopt.h>
55 #include <string.h>
56 #include <errno.h>
57
58 #if defined(__linux__)  &&  __GNU_LIBRARY__ == 1
59 #include <asm/io.h>                     /* Newer libraries use <sys/io.h> instead. */
60 #else
61 #include <sys/io.h>
62 #endif
63
64 /* Two functions that some GLIBC header file sets omit.  grrr... */
65 extern int ioperm(unsigned long __base, unsigned long extent, int on_or_off);
66 extern int iopl(int level);
67
68 struct option longopts[] = {
69  /* { name      has_arg  *flag  val } */
70         {"base-address", 1, 0, 'p'}, /* Base I/O port address. */
71         {"help",           0, 0, 'h'},  /* Give help */
72         {"interface",  0, 0, 'F'},      /* Set the transceiver type (built-in, AUI) */
73         {"NE2000",         1, 0, 'N'},  /* Set to NE2000 mode */
74         {"new_ioport", 1, 0, 'P'},      /* Set a I/O location for the card. */
75         {"irq",            1, 0, 'Q'},  /* Set a new IRQ (interrupt) line. */
76         {"wd8013",         1, 0, 'W'},  /* Set to WD8013 mode */
77         {"verbose",        0, 0, 'v'},  /* Verbose mode */
78         {"version",        0, 0, 'V'},  /* Display version number */
79         {"write-EEPROM", 1, 0, 'w'},/* Write th EEPROMS with the specified vals */
80         { 0, 0, 0, 0 }
81 };
82
83 /* Three configuration registers are read and may be set from the EEPROM.
84    They are named registers A, B and C
85
86    RegA         NE/WD   FREAD  INT2   INT1   INT0  IOAD2  IOAD1  IOAD0
87    RegB         EELOAD  BTPR   BUSE   CHRDY  IO16  GDLINK XCVR1  XCVR0
88    RegC         SoftEn  ClkSel IntMde COMP   BtPR3 BtPR2  BtPR1  BtPROM0
89 */
90 /* I/O base settings, IOAD2-0, in register A. */
91 static int io_base[8] = {
92         0x300, 0x278, 0x240, 0x280, 0x2C0, 0x320, 0x340, 0x360};
93 /* The IRQ line settings, INT2-0, in register A, and a reverse map. */
94 static int index2irq[8] = {3, 4, 5, 9, 10, 11, 12, 15};
95 static int irq2index[16] = {-1, -1, 3, 0, 1, 2, -1, -1,
96                                                         -1, 3, 4, 5, 6, -1, -1, 7};
97 /* The transceiver settings, XCVR1-0, in register B.
98    10baseT-LRT is Low Receive Threshold -- "turning the volume up" for long
99    wiring runs.
100 */
101 static char *xcvr_name[4] = {"10baseT", "Thinnet", "AUI", "10baseT (LRT)"};
102
103 int opt_f = 0;
104
105 static void show_config(int regA, int regB);
106 static void write_EEPROM(int dp8390_base, int regA, int regB, int regC);
107
108 \f
109 /*
110  I should say something here... uuhhhhmmm....
111  */
112
113 int
114 main(int argc, char *argv[])
115 {
116         extern char *optarg;
117         int port_base = 0x300;          /* Base address of the board. */
118         int     ioaddr = 0x300;                 /* Base address of the 8390 registers. */
119         int new_ioport = -1, new_irq = -1, new_mode = 0;
120         int errflag = 0, verbose = 0, wd_mode = 0, write_eeprom = 0;
121         int show_version = 0, opt_a = 0;
122         int new_io_idx = -1, xcvr = -1; /* I/O port, Transceiver type to set. */
123         int regA, regB, old_regA, old_regB;
124         int option;
125
126         while ((option = getopt(argc, argv, "afF:hi:Q:m:Np:P:vVwW")) != -1)
127                 switch (option) {
128                 case 'a':  opt_a++; break;
129                 case 'f':  opt_f++; break;
130                 case 'F':
131                         if (strncmp(optarg, "10base", 6) == 0) {
132                                 switch (optarg[6]) {
133                                 case 'T':  xcvr = 0; break;
134                                 case '2':  xcvr = 1; break;
135                                 case '5':  xcvr = 2; break;
136                                 default: errflag++;
137                                 }
138                         } else if (strcmp(optarg, "AUI") == 0)
139                                 xcvr = 2;
140                         else if (optarg[0] >= '0' &&  optarg[0] <= '3'
141                                          &&  optarg[1] == 0)
142                                 xcvr = optarg[0] - '0';
143                         else {
144                                 fprintf(stderr, "Invalid interface specified: it must be"
145                                                 " 0..3, '10base{T,2,5}' or 'AUI'.\n");
146                                 errflag++;
147                         }
148                         break;
149                 case 'h':  printf(usage_msg); return 0;
150                 case 'N':  wd_mode = 0; new_mode = 2; break;
151                 case 'i':
152                 case 'Q':
153                         new_irq = atoi(optarg);
154                         if (new_irq < 3 || new_irq > 15 || new_irq == 6 || new_irq == 8) {
155                                 fprintf(stderr, "Invalid new IRQ %#x.  Valid values: "
156                                                 "3-5,7,9-15.\n", new_irq);
157                                 errflag++;
158                         }
159                         break;
160                 case 'p':  port_base = strtol(optarg, NULL, 16);  break;
161                 case 'P':  new_ioport = strtol(optarg, NULL, 16);  break;
162                 case 'v':  verbose++;    break;
163                 case 'V':  show_version++;               break;
164                 case 'w':  write_eeprom++;               break;
165                 case 'W':  wd_mode++; new_mode = 1; break;
166                 case '?':
167                         errflag++;
168                 }
169         if (errflag) {
170                 fprintf(stderr, usage_msg);
171                 return 3;
172         }
173
174         if (verbose || show_version)
175                 printf(version_msg);
176
177         /* Turn on access to the I/O ports. */
178         if (ioperm(port_base, 32, 1) < 0) {
179                 perror("atlantic: io-perm");
180                 fprintf(stderr, "       (You must run this program with 'root' permissions.)\n");
181                 return 2;
182         }
183
184         /* Check the specified new I/O port value. */
185         if (new_irq >= 0  &&  (new_irq >= 16  ||  irq2index[new_irq] < 0)) {
186                 fprintf(stderr, "The new IRQ line, %d, is invalid.\n",
187                                 new_irq);
188                 return 3;
189         }
190         if (new_ioport >= 0) {
191                 int i;
192                 for (i = 0; i < 8; i++)
193                         if (new_ioport == io_base[i])
194                                  break;
195                 if (i >= 8) {
196                         fprintf(stderr, "The new base I/O address, %#x, is invalid.\n",
197                                         new_ioport);
198                         return 3;
199                 }
200                 new_io_idx = i;
201         }
202
203         if ( ! opt_f  &&  inb(port_base) == 0xff) {
204                 printf("No AT/LANTIC chip found at I/O %#x.\n"
205                            " Use '-f' to override if you are certain the chip is at this"
206                            " I/O location.\n", port_base);
207                 return 1;
208         }
209                 
210         /* First find if this card is set to WD or NE mode by locating the 8390
211            registers. */
212         {
213                 int saved_cmd = inb(port_base);
214                 int cntr;
215                 outb(0x20, port_base);
216                 inb(port_base + 13);    /* Clear a counter register */
217                 cntr = inb(port_base + 13);
218                 if (verbose)
219                         printf("  The NE2K 8390 cmd register was %2.2x, cntr %d.\n",
220                                    saved_cmd, cntr);
221                 if (cntr)
222                         ioaddr = port_base + 16;
223                 else
224                         ioaddr = port_base;
225                 outb(saved_cmd, port_base);
226         }
227
228         if (opt_a) {
229                 int saved_0 = inb(port_base);
230                 int window, i;
231
232                 printf("8390 registers at %#x (command register was %2.2x)\n",
233                            ioaddr, saved_0);
234                 for (window = 0; window < 4; window++) {
235                         printf(" Window %d:", window);
236                         outb((window<<6) | 0x20, ioaddr);
237                         for(i = 0; i < 16; i++) {
238                                 if (window == 0 && i == 6)
239                                         printf(" **");
240                                 else
241                                         printf(" %2.2x", inb(ioaddr + i));
242                         }
243                         printf(".\n");
244                 }
245                 if (ioaddr != port_base) {                              /* WD emulation. */
246                         printf("WD8013 compatible registers: ");
247                         for(i = 0; i < 16; i++) {
248                                 printf(" %2.2x", inb(port_base + i));
249                                 fflush(stdout);
250                         }
251                         printf(".\n");
252                 }
253         }
254
255         printf("Reading the configuration from the AT/LANTIC at %#3x...\n",
256                    port_base);
257         outb(0x21, ioaddr);                     /* Select Window 0 */
258         old_regA = regA = inb(ioaddr + 0x0A);
259         old_regB = regB = inb(ioaddr + 0x0B);
260
261         printf("The current configuration is\n");
262         show_config(regA, regB);
263
264         /* This should never be triggered.  We see the card after all!*/
265         if ((regA & 7) == 1)
266                 printf(" Your card is set to be configured at boot-time.\n"
267                            "  Remaining with this software configuration is a bad idea"
268                            " when using\n  modern systems.  The card will likely be"
269                            " unintentionally activated at\n  unexpected I/O and IRQ"
270                            " settings.\n" );
271
272         if (new_mode)
273                 regA = (regA & ~0x80) | (new_mode == 1  ? 0x80 : 0);
274         if (new_irq >= 0)
275                 regA = (regA & ~0x38) | (irq2index[new_irq]<<3);
276         if (new_io_idx >= 0)
277                 regA = (regA & ~0x07) | new_io_idx;
278         if (xcvr >= 0)
279                 regB = (regB & 0x5C) | (xcvr & 0x3);
280
281         if (regA != old_regA || regB != old_regB || write_eeprom) {
282                 printf("The proposed new configuration is\n");
283                 show_config(regA, regB);
284                 if ( ! write_eeprom)
285                         printf(" Use '-w' to set this as the current configuration "
286                                    "(valid until next reset).\n"
287                                    " Use '-w -w' to write the settings to the EEPROM.\n"
288                                    " Use '-w -w -w' to do both.\n");
289         }
290
291         /* We must write the EEPROM and register B before possibly moving the
292            I/O base. */
293         if (write_eeprom > 1) {
294                 printf("Writing the new configuration to the EEPROM...\n");
295                 write_EEPROM(ioaddr, regA, regB, 0x30);
296                 printf("Wrote the EEPROM at %#3x with 0x%2.2x 0x%2.2x 0x%2.2x.\n",
297                            port_base, regA, regB, 0x30);
298         }
299
300         if (write_eeprom & 1) {
301                 if (regB != old_regB) {
302                         printf(" Setting register B to %#02x.\n", regB);
303                         inb(ioaddr + 0x0B);
304                         outb(regB, ioaddr + 0x0B);
305                         regB = inb(ioaddr + 0x0B);
306                         printf(" Register B is now %#02x:  Interface %s.\n", regB,
307                                    xcvr_name[regB & 0x03]);
308                 }
309                 if (regA != old_regA) {
310                         printf(" Setting register A to %#02x (%s mode I/O %#x IRQ %d).\n",
311                                    regA, regA & 0x80 ? "WD8013" : "NE2000",
312                                    io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07] );
313                         inb(ioaddr + 0x0A);
314                         outb(regA, ioaddr + 0x0A);
315                         if (port_base == io_base[regA&7]) {
316                                 ioaddr = port_base + (regA & 0x80 ? 16 : 0);
317                                 printf(" Register A at base %x is now %#02x.\n",
318                                            ioaddr, inb(ioaddr + 0x0A));
319                         }
320                 }
321         }
322
323         return 0;
324 }
325
326 static void show_config(int regA, int regB)
327 {
328         printf("  Register A 0x%2.2x:  I/O base @ %#x, IRQ %d,"
329                    " %s mode, %s ISA read.\n",
330                    regA, io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07],
331                    regA & 0x80 ? "WD8013" : "NE2000",
332                    regA & 0x40 ? "fast" : "normal" );
333         printf("  Register B 0x%2.2x:  Interface %s.\n", regB,
334                    xcvr_name[regB & 0x03]);
335         printf("    Boot PROM writes are %sabled, CHRDY after %s%s.\n",
336                    regB & 0x40 ? "en" : "dis",
337                    regB & 0x10 ? "BALE" : "IORD/IOWR",
338                    regB & 0x08 ? ", IO16 after IORD/IOWR" : "");
339         if (regB & 0x20)
340                 printf("    Bus error detected.\n");
341 }
342
343 /* Write the A, B, and C configuration registers into the EEPROM at
344    DP8390_BASE.
345    Ideally this would be done with interrupts disabled, but we do not have
346    that capability with user level code.
347 */
348 static void write_EEPROM(int dp8390_base, int regA, int regB, int regC)
349 {
350         int ioregb = dp8390_base + 0x0B;
351         int currB = inb(ioregb) & (~0x04) ;
352
353         outb(currB | 0x80, ioregb);
354         inb(ioregb);
355         outb(regA, ioregb);
356         outb(regB, ioregb);
357         outb(regC, ioregb);
358         return;
359 }
360 \f
361 /*
362  * Local variables:
363  *  compile-command: "gcc -Wall -Wstrict-prototypes -O6 -o atlantic atlantic.c"
364  *  c-indent-level: 4
365  *  c-basic-offset: 4
366  *  tab-width: 4
367  * End:
368  */
This page took 0.099809 seconds and 2 git commands to generate.