]>
Commit | Line | Data |
---|---|---|
402a159c | 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 | */ |