1 /* 3c5x9setup.c: Setup program for 3Com EtherLink III ethercards.
3 Copyright 1994-1999 by Donald Becker <becker@cesdis.gsfc.nasa.gov>
4 Modified 2000 by RedHog (Egil Möller) <redhog@lysator.liu.se>
5 This version released under the Gnu Public License, incorporated herein
6 by reference. Contact the author for use under other terms.
8 This is a EEPROM setup and diagnostic program for the 3Com 3c5x9 series
9 ethercards. Most products with "EtherLink III" in the name are supported,
11 3c509, 3c529, 3c579 (ISA, MCA, EISA, but not PCI)
12 3c556 3c562, 3c563, 3c574 and other PCMCIA (but not CardBus) cards
13 The 'B' and 'C' suffix versions are supported, as are the various
14 transceiver options (e.g. "-TPO")
17 http://cesdis.gsfc.nasa.gov/linux/diag/index.html
18 http://3c5x9.redhog.org
20 This program must be compiled with "-O"! See the bottom of this file
21 for the suggested compile-command.
23 The author may be reached as becker@cesdis.gsfc.nasa.gov.
24 C/O USRA-CESDIS, Code 930.5 Bldg. 28, Nimbus Rd., Greenbelt MD 20771
27 The 3Com EtherLink III manual, available from 3Com
28 http://cesdis.gsfc.nasa.gov/linux/diag/index.html
31 static char *version_msg =
32 "3c5x9setup.c:donald.becker.0.05b.redhog.1 2/5/00 RedHog (Egil Möller) <redhog@lysator.liu.se>\n";
33 static char *usage_msg =
34 "Usage: 3c5x9setup [-aEfFsvVw] [-p <IOport>] [-F 10baseT|10base2|AUI>] [-Q <IRQ>] [-d <filename>] [-r <filename>] [-u <UMID>] [-m <MAC address>]\n";
37 #warning You must compile this program with the correct options!
38 #warning See the last lines of the source file.
39 #error You must compile this driver with "-O".
47 #include <sys/types.h>
51 #if defined(__linux__) && __GNU_LIBRARY__ == 1
52 #include <asm/io.h> /* Newer libraries use <sys/io.h> instead. */
55 /* Use extern iopl(int level); if your glibc does not define it. */
58 /* We should use __u8 .. __u32, but they are not always defined. */
59 typedef unsigned int u32;
60 typedef unsigned short u16;
61 typedef unsigned char u8;
63 struct option longopts[] = {
64 /* { name has_arg *flag val } */
65 {"base-address", 1, 0, 'p'},
66 {"new-base-address", 1, 0, 'P'},
67 {"show-all-registers", 0, 0, 'a'}, /* Print all registers. */
69 {"show-eeprom", 0, 0, 'e'}, /* Dump EEPROM contents (-ee valid). */
70 {"help", 0, 0, 'h'}, /* Give help */
71 {"emergency-rewrite", 0, 0, 'E'}, /* Re-write a corrupted EEPROM. */
72 {"force-detection", 0, 0, 'f'},
73 {"new-interface", 1, 0, 'F'}, /* New interface (built-in, AUI, etc.) */
74 {"new-IOaddress", 1, 0, 'P'}, /* New base I/O address. */
75 {"new-irq", 1, 0, 'Q'}, /* New interrupt number */
76 {"verbose", 0, 0, 'v'}, /* Verbose mode */
77 {"version", 0, 0, 'V'}, /* Display version number */
78 {"write-EEPROM", 1, 0, 'w'},/* Actually write the EEPROM with new vals */
79 {"dump", 1, 0, 'd'}, /* Dump EEPROM content to file */
80 {"restore", 1, 0, 'r'}, /* Restore EEPROM content from fale */
81 {"new-hw", 1, 0, 'm'}, /* Sets the MAC adress of the card */
82 {"new-muid", 1, 0, 'u'}, /* Sets the Unique identifier of the card */
86 /* Offsets from base I/O address. */
88 #define EL3_STATUS 0x0e
91 Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
92 Wn0EepromData = 12, /* Window 0: EEPROM results register. */
93 IntrStatus=0x0E, /* Valid in all windows. */
96 #define EEPROM_READ 0x80
97 #define EEPROM_WRITE 0x40
98 #define EEPROM_ERASE 0xC0
99 #define EEPROM_EWENB 0x30 /* Enable erasing/writing for 10 msec. */
100 #define EEPROM_EWDIS 0x00 /* Enable erasing/writing for 10 msec. */
101 enum Win0_EEPROM_bits {
102 EEPROM_Read = 0x80, EEPROM_Busy = 0x8000,
105 #define EL3WINDOW(win_num) outw(0x0800+(win_num), ioaddr + EL3_CMD)
107 /* Register window 1 offsets, the window used in normal operation. */
109 TX_FIFO = 0x0, RX_FIFO = 0x0, Wn1RxErrors = 0x4,
110 Wn1RxStatus = 0x8, Wn1Timer=0xA, Wn1TxStatus = 0xB,
111 Wn1TxFree = 0xC, /* Remaining free bytes in Tx buffer. */
114 const char *intr_names[13] ={
115 "Interrupt latch", "Adapter Failure", "Tx Complete", "Tx Available",
116 "Rx Complete", "Rx Early Notice", "Driver Intr Request",
117 "Statistics Full", "DMA Done", "Download Complete", "Upload Complete",
118 "DMA in Progress", "Command in Progress",
121 /* EEPROM operation locations. */
123 PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
124 EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
125 AltPhysAddr01=10, AltPhysAddr23=11, AltPhysAddr45=12,
126 DriverTune=13, Checksum=15};
128 /* Last-hope recovery major boo-boos: rewrite the EEPROM with the values
129 from my card (and hope I don't met you on the net...).
130 This image is valid only for an pre-B 3c509.
132 unsigned short djb_eeprom[16] = {
133 0x0020, 0xaf0e, 0x3bc2, 0x9058, 0xbc4e, 0x0036, 0x4441, 0x6d50,
134 0x0090, 0xaf00, 0x0020, 0xaf0e, 0x3bc2, 0x1310, 0x0000, 0x343c, };
135 /* Values read from the EEPROM, and the new image. */
136 #define EEPROM_SPACE 64
137 unsigned short eeprom_contents[EEPROM_SPACE];
138 unsigned short new_ee_contents[EEPROM_SPACE];
140 int verbose = 1, opt_f = 0, debug = 0;
141 int show_regs = 0, show_eeprom = 0;
142 int do_write_eeprom = 0;
145 const char *intrs_pending_msg =
146 " This network adapter has unhandled interrupts!\n"
147 " This should never occur with properly configured adapter. You may have\n"
148 " a hardware interrupt conflict.\n"
149 " Check /proc/interrupts to verify that the interrupt is properly\n"
150 " registered, and that the count is increasing.\n"
151 " This problem is frequently solved by moving the adapter to different IRQ.\n"
152 " For ISA cards, verify that the BIOS setup has assigned the IRQ line to\n"
154 " For PCMCIA cards, change the configuration file, typically named\n"
155 " /etc/pcmcia/config.opts, to use a different IRQ.\n";
157 static void print_eeprom(unsigned short *eeprom_contents);
158 static void write_eeprom(short ioaddr, int index, int value);
159 static unsigned int calculate_checksum(unsigned short *values);
160 static int do_update(unsigned short *ee_values,
161 int index, char *field_name, int new_value);
164 main(int argc, char **argv)
166 int port_base = 0x300;
167 int new_interface = -1, new_irq = -1, new_ioaddr = -1;
168 int errflag = 0, show_version = 0;
169 int emergency_rewrite = 0;
170 int show_regs = 0, opt_a = 0;
171 char *dumpcard = NULL;
172 char *restorecard = NULL;
174 char *new_umid = NULL;
175 int c, longind, i, j, saved_window;
178 while ((c = getopt_long(argc, argv, "aDeEfF:hi:p:P:Q:svVwX:d:r:o:m:",
182 case 'a': show_regs++; opt_a++; break;
183 case 'D': debug++; break;
184 case 'e': show_eeprom++; break;
185 case 'E': emergency_rewrite++; break;
186 case 'f': opt_f++; break;
188 if (strncmp(optarg, "10base", 6) == 0) {
190 case 'T': new_interface = 0; break;
191 case '2': new_interface = 3; break;
192 case '5': new_interface = 1; break;
195 } else if (strcmp(optarg, "AUI") == 0)
197 else if (optarg[0] >= '0' && optarg[0] <= '3'
199 new_interface = optarg[0] - '0';
201 fprintf(stderr, "Invalid interface specified: it must be"
202 " 0..3, '10base{T,2,5}' or 'AUI'.\n");
207 new_irq = atoi(optarg);
208 if (new_irq < 3 || new_irq > 15 || new_irq == 6 || new_irq == 8) {
209 fprintf(stderr, "Invalid new IRQ %#x. Valid values: "
210 "3-5,7,9-15.\n", new_irq);
215 port_base = strtol(optarg, NULL, 16);
218 new_ioaddr = strtol(optarg, NULL, 16);
219 if (new_ioaddr < 0x200 || new_ioaddr > 0x3f0) {
220 fprintf(stderr, "Invalid new I/O address %#x. Valid range "
221 "0x200-0x3f0.\n", new_ioaddr);
225 case 'v': verbose++; break;
226 case 'V': show_version++; break;
227 case 'w': do_write_eeprom++; break;
234 restorecard = optarg;
244 fprintf(stderr, usage_msg);
248 if (ioperm(port_base, 16, 1) < 0) {
249 perror("3c5x9setup: ioperm()");
250 fprintf(stderr, "This program must be run as root.\n");
259 saved_window = inw(ioaddr + EL3_STATUS);
260 if (saved_window == 0xffff) {
261 printf("No EtherLink III device exists at address 0x%X.\n", ioaddr);
263 printf("Use the '-f' option proceed anyway.\n");
268 /* We can check for a stuck interrupt even while the chip is active. */
269 if (verbose || opt_a) {
270 unsigned intr_status = inw(ioaddr + IntrStatus);
271 printf(" %snterrupt sources are pending.\n",
272 (intr_status & 0x03ff) ? "I": "No i");
273 if (intr_status & 0x3ff) {
274 for (i = 0; i < 13; i++)
275 if (intr_status & (1<<i))
276 printf(" %s indication.\n", intr_names[i]);
279 if (!opt_f && (saved_window & 0xe000) == 0x2000) {
280 int last_intr = inb(ioaddr + Wn1Timer);
281 printf("A potential 3c5*9 has been found, but it appears to still be "
282 "active.\nOnly limited information is available without "
283 "disturbing network operation.\n"
284 " Either shutdown the network, or use the '-f' flag to see all "
286 printf(" Available Tx room %d bytes, Tx/Rx Status %4.4x / %4.4x.\n",
287 inw(ioaddr + Wn1TxFree), inb(ioaddr + Wn1TxStatus),
288 inb(ioaddr + Wn1RxStatus));
289 if (last_intr != 255)
290 printf(" An interrupt occured only %d ticks ago!\n", last_intr);
291 if (saved_window & 0x00ff)
292 printf(intrs_pending_msg);
297 if (inw(port_base) == 0x6d50) {
298 printf("3c5x9 found at %#3.3x.\n", port_base);
300 printf("3c5*9 not found at %#3.3x, status %4.4x.\n"
301 "If there is a 3c5*9 card in the machine, explicitly set the"
302 " I/O port address\n using '-p <ioaddr>\n",
303 port_base, inw(port_base));
309 printf(" Indication enable is %4.4x, interrupt enable is %4.4x.\n",
310 inw(ioaddr + 12), inw(ioaddr + 10));
313 const char *statname[12] = {
314 "Carrier Lost", "Heartbeat", "Tx Multiple collisions",
315 "Tx Single collisions", "Tx Late collisions", "Rx FIFO overruns",
316 "Tx packets", "Rx packets", "Tx deferrals",
317 "(256 Rx/Tx counts)", "Rx bytes", "Tx bytes"};
319 /* First read the statistics registers, which are clear-on-read. */
321 for (i = 0; i < 10; i++)
322 stats[i] = inb(ioaddr + i);
323 stats[10] = inw(ioaddr + 10);
324 stats[11] = inw(ioaddr + 12);
325 for (i = 0; i < 12; i++)
327 printf(" Event counts: %s is %d.\n", statname[i], stats[i]);
331 for (j = 0; j < 8; j++) {
333 printf(" Window %d:", j);
334 outw(0x0800 + j, ioaddr + 0x0e);
335 for (i = 0; i < 16; i+=2)
336 printf(" %4.4x", inw(ioaddr + i));
343 /* Read the EEPROM. */
344 for (i = 0; i < EEPROM_SPACE; i++) {
346 outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
347 /* Pause for up to 162 us. for the read to take place.
348 Typical max. 175 ticks. */
349 for (boguscheck = 0; boguscheck < 1620; boguscheck++)
350 if ((inw(ioaddr + Wn0EepromCmd) & EEPROM_Busy) == 0)
352 eeprom_contents[i] = inw(ioaddr + Wn0EepromData);
354 printf("EEPROM read of index %d %s after %d ticks -> %4.4x.\n",
355 i, boguscheck < 1620 ? "completed" : "failed", boguscheck,
359 if (emergency_rewrite) {
360 if (emergency_rewrite < 3 || !do_write_eeprom)
361 printf(" Caution! Last-chance EEPROM write requested. The\n"
362 " new EEPROM values will not be written without"
363 " '-E -E -E -w' flags.\n");
365 for (i = 0; i < 16; i++) {
366 eeprom_contents[i] = djb_eeprom[i];
367 write_eeprom(ioaddr, i, eeprom_contents[i]);
377 if ((fd = open(restorecard, O_RDONLY)))
379 for (i = 0; i < 16; i++)
381 while ((ret = read(fd, (char *) (eeprom_contents + i), 1)) == 0);
384 while ((ret = read(fd, ((char *) (eeprom_contents + i)) + 1, 1)) == 0);
390 perror("Unable to read EEPROM image from file.");
394 fprintf(stderr, "EEPROM image read from file '%s'.\n", restorecard);
399 perror("Unable to read EEPROM image from file.");
404 for (i = 0; i < 16; i++)
405 write_eeprom(ioaddr, i, eeprom_contents[i]);
406 fprintf(stderr, "EEPROM image written to card.\n");
409 fprintf(stderr, "Specify -w to write image to card.\n");
413 unsigned short new_ifxcvrio = eeprom_contents[IFXcvrIO];
414 unsigned short new_irqline = eeprom_contents[IRQLine];
415 int something_changed = 0;
417 if (new_interface >= 0)
418 new_ifxcvrio = (new_interface << 14) | (new_ifxcvrio & 0x3fff);
420 new_ifxcvrio = ((new_ioaddr>>4) & 0x1f) | (new_ifxcvrio & 0xffe0);
422 new_irqline = (new_irq << 12) | 0x0f00;
424 if (do_update(eeprom_contents, IRQLine, "IRQ", new_irqline))
427 if (do_update(eeprom_contents, IFXcvrIO, "transceiver/IO",
433 unsigned short mac_address_tmp[6];
434 unsigned char mac_address[6];
436 sscanf(new_hw, "%X:%X:%X:%X:%X:%X",
437 (unsigned int* ) (mac_address_tmp + 0), (unsigned int* ) (mac_address_tmp + 1),
438 (unsigned int* ) (mac_address_tmp + 2), (unsigned int* ) (mac_address_tmp + 3),
439 (unsigned int* ) (mac_address_tmp + 4), (unsigned int* ) (mac_address_tmp + 5));
441 for (i = 0; i < 6; i++)
442 mac_address[i] = (unsigned char) mac_address_tmp[i^1];
444 do_update(eeprom_contents, PhysAddr01, "MACAddress", ((unsigned short*) mac_address)[0]);
445 do_update(eeprom_contents, PhysAddr23, "MACAddress", ((unsigned short*) mac_address)[1]);
446 do_update(eeprom_contents, PhysAddr45, "MACAddress", ((unsigned short*) mac_address)[2]);
451 unsigned short mac_address_tmp[6];
452 unsigned char mac_address[6];
454 sscanf(new_umid, "%X:%X:%X:%X:%X:%X",
455 (unsigned int* ) (mac_address_tmp + 0), (unsigned int* ) (mac_address_tmp + 1),
456 (unsigned int* ) (mac_address_tmp + 2), (unsigned int* ) (mac_address_tmp + 3),
457 (unsigned int* ) (mac_address_tmp + 4), (unsigned int* ) (mac_address_tmp + 5));
459 for (i = 0; i < 6; i++)
460 mac_address[i] = (unsigned char) mac_address_tmp[i^1];
462 do_update(eeprom_contents, AltPhysAddr01, "UMID", ((unsigned short*) mac_address)[0]);
463 do_update(eeprom_contents, AltPhysAddr23, "UMID", ((unsigned short*) mac_address)[1]);
464 do_update(eeprom_contents, AltPhysAddr45, "UMID", ((unsigned short*) mac_address)[2]);
467 /* To change another EEPROM value write it here. */
469 if (do_update(eeprom_contents, Checksum, "checksum",
470 calculate_checksum(eeprom_contents)))
473 if (something_changed && !do_write_eeprom)
474 printf(" (The new EEPROM values will not be written without"
475 " the '-w' flag.)\n");
478 if (verbose > 1 || show_eeprom) {
479 print_eeprom(eeprom_contents);
487 if ((fd = open(dumpcard, O_WRONLY | O_CREAT | O_TRUNC)))
489 for (i = 0; i < 16; i++)
491 while ((ret = write(fd, (char *) (eeprom_contents + i), 1)) == 0);
494 while ((ret = write(fd, ((char *) (eeprom_contents + i)) + 1, 1)) == 0);
499 perror("Unable to write EEPROM image to file.");
501 fprintf(stderr, "EEPROM image written to file '%s'.\n", dumpcard);
504 perror("Unable to write EEPROM image to file.");
508 EL3WINDOW(saved_window>>13);
513 static void print_eeprom(unsigned short *eeprom_contents)
515 char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
516 u16 *ee = eeprom_contents;
519 printf(" EEPROM contents:");
521 for(i = 0; i < EEPROM_SPACE; i++)
522 printf("%s %4.4x", i % 8 ? "" : "\n ", eeprom_contents[i]);
523 printf("\n Model number 3c%2.2x%1.1x version %1.1x, base I/O %#x, IRQ %d, "
525 eeprom_contents[ModelID] & 0x00ff,
526 eeprom_contents[ModelID] >> 12,
527 (eeprom_contents[ModelID] >> 8) & 0x000f,
528 0x200 + ((eeprom_contents[IFXcvrIO] & 0x1f) << 4),
529 eeprom_contents[IRQLine] >> 12,
530 if_names[eeprom_contents[IFXcvrIO] >> 14]);
532 printf(" 3Com Node Address (UMID) ");
533 for (i = 0; i < 6; i++)
534 printf("%2.2X%s", ((unsigned char *)(eeprom_contents + AltPhysAddr01))[i^1], i < 5 ? ":" : " (used as a unique ID only).\n");
535 printf(" OEM Station address (MAC) ");
536 for (i = 0; i < 6; i++)
537 printf("%2.2X%s", ((unsigned char *)(eeprom_contents + PhysAddr01))[i^1], i < 5 ? ":" : " (used as the ethernet address).\n");
539 /* Y2K safe. This two-digit date is for information purposes only.
540 It is never used for computations. */
541 printf(" Manufacture date (MM/DD/YY) %d/%d/%d, division %c,"
542 " product %c%c.\n", (ee[4] >> 5) & 15,
543 ee[4] & 31, ee[4] >> 9, ((unsigned char *)(eeprom_contents + PhysAddr01))[10], ((unsigned char *)(eeprom_contents + PhysAddr01))[12], ((unsigned char *)(eeprom_contents + PhysAddr01))[13]);
544 printf(" Options: %s duplex, %sable linkbeat.\n",
545 ee[13] & 0x8000 ? "force full" : "half",
546 ee[13] & 0x4000 ? "dis" : "en");
548 if (calculate_checksum(eeprom_contents) != eeprom_contents[Checksum])
549 printf("****CHECKSUM ERROR****: Calcuated checksum: %4.4x, "
550 "stored checksum %4.4x.\n",
551 calculate_checksum(eeprom_contents),
552 eeprom_contents[Checksum]);
554 printf(" The computed checksum matches the stored checksum of %4.4x.\n",
555 eeprom_contents[Checksum]);
559 static void write_eeprom(short ioaddr, int index, int value)
562 /* Verify that the EEPROM is idle. */
563 for (timer = 1620; inw(ioaddr + Wn0EepromCmd) & 0x8000;)
566 outw(EEPROM_EWENB, ioaddr + Wn0EepromCmd);
568 outw(EEPROM_ERASE + index, ioaddr + Wn0EepromCmd);
570 outw(EEPROM_EWENB, ioaddr + Wn0EepromCmd);
572 outw(value, ioaddr + Wn0EepromData);
573 outw(EEPROM_WRITE + index, ioaddr + Wn0EepromCmd);
574 for (timer = 16000; inw(ioaddr + Wn0EepromCmd) & 0x8000;)
578 fprintf(stderr, "EEPROM wrote index %d with 0x%4.4x after %d ticks!\n",
579 index, value, 16000-timer);
582 fprintf(stderr, "Failed to write EEPROM location %d with 0x%4.4x!\n",
586 /* Calculate the EEPROM checksum.
587 The checksum for the fixed values is returned in the high byte.
588 The checksum for the programmable variables is in the low the byte.
592 calculate_checksum(unsigned short *values)
594 int fixed_checksum = 0, var_checksum = 0;
597 for (i = 0; i <= 14; i++) { /* Note: 14 (loc. 15 is the sum) */
598 if (i == IFXcvrIO || i == IRQLine || i == DriverTune)
599 var_checksum ^= values[i];
601 fixed_checksum ^= values[i];
603 return ((fixed_checksum ^ (fixed_checksum << 8)) & 0xff00) |
604 ((var_checksum ^ (var_checksum >> 8)) & 0xff);
607 static int do_update(unsigned short *ee_values,
608 int index, char *field_name, int new_value)
610 if (ee_values[index] != new_value) {
611 if (do_write_eeprom) {
612 printf("Writing new %s entry 0x%4.4x.\n",
613 field_name, new_value);
614 write_eeprom(ioaddr, index, new_value);
616 printf(" Would write new %s entry 0x%4.4x (old value 0x%4.4x).\n",
617 field_name, new_value, ee_values[index]);
618 ee_values[index] = new_value;
627 * compile-command: "cc -O -Wall -o 3c5x9setup 3c5x9setup.c"