]>
git.ipfire.org Git - people/pmueller/ipfire-3.x.git/blob - net-tools/mii-diag.c
2 * mii-diag.c: Examine and set the MII registers of a network interfaces.
4 Usage: mii-diag [-vw] interface.
6 This program reads and writes the Media Independent Interface (MII)
7 management registers on network transceivers. The registers control
8 and report network link settings and errors. Examples are link speed,
9 duplex, capabilities advertised to the link partner, status LED
10 indications and link error counters.
13 The compile-command is at the end of this source file.
14 This program works with drivers that implement MII ioctl() calls.
16 Written/copyright 1997-2003 by Donald Becker <becker@scyld.com>
18 This program is free software; you can redistribute it
19 and/or modify it under the terms of the GNU General Public
20 License as published by the Free Software Foundation.
22 The author may be reached as becker@scyld.com, or C/O
23 Scyld Computing Corporation
24 914 Bay Ridge Road, Suite 220
28 http://scyld.com/expert/mii-status.html
29 http://scyld.com/expert/NWay.html
30 http://www.national.com/pf/DP/DP83840.html
33 static char version
[] =
34 "mii-diag.c:v2.11 3/21/2005 Donald Becker (becker@scyld.com)\n"
35 " http://www.scyld.com/diag/index.html\n";
37 static const char usage_msg
[] =
38 "Usage: %s [--help] [-aDfrRvVw] [-AF <speed+duplex>] [--watch] <interface>\n";
39 static const char long_usage_msg
[] =
40 "Usage: %s [-aDfrRvVw] [-AF <speed+duplex>] [--watch] <interface>\n\
42 This program configures and monitors the transceiver management registers\n\
43 for network interfaces. It uses the Media Independent Interface (MII)\n\
44 standard with additional Linux-specific controls to communicate with the\n\
45 underlying device driver. The MII registers control and report network\n\
46 link settings and errors. Examples are link speed, duplex, capabilities\n\
47 advertised to the link partner, status LED indications and link error\n\
50 The common usage is\n\
53 Frequently used options are\n\
54 -A --advertise <speed|setting>\n\
55 -F --fixed-speed <speed>\n\
56 Speed is one of: 100baseT4, 100baseTx, 100baseTx-FD, 100baseTx-HD,\n\
57 10baseT, 10baseT-FD, 10baseT-HD\n\
58 -s --status Return exit status 2 if there is no link beat.\n\
60 Less frequently used options are\n\
61 -a --all-interfaces Show the status all interfaces\n\
62 (Not recommended with options that change settings.)\n\
64 -g --read-parameters Get driver-specific parameters.\n\
65 -G --set-parameters PARMS Set driver-specific parameters.\n\
66 Parameters are comma separated, missing parameters retain\n\
67 their previous values.\n\
68 -M --msg-level LEVEL Set the driver message bit map.\n\
69 -p --phy ADDR Set the PHY (MII address) to report.\n\
70 -r --restart Restart the link autonegotiation.\n\
71 -R --reset Reset the transceiver.\n\
72 -v --verbose Report each action taken.\n\
73 -V --version Emit version information.\n\
74 -w --watch Continuously monitor the transceiver and report changes.\n\
76 This command returns success (zero) if the interface information can be\n\
77 read. If the --status option is passed, a zero return means that the\n\
78 interface has link beat.\n\
89 #include <sys/types.h>
90 #include <sys/socket.h>
91 #include <sys/ioctl.h>
93 #ifdef use_linux_libc5
94 #include <linux/if_arp.h>
95 #include <linux/if_ether.h>
98 typedef u_int32_t u32
;
99 typedef u_int16_t u16
;
102 #if defined(SIOCGPARAMS) && SIOCGPARAMS != SIOCDEVPRIVATE+3
103 #error Changed definition for SIOCGPARAMS
105 #define SIOCGPARAMS (SIOCDEVPRIVATE+3) /* Read operational parameters. */
106 #define SIOCSPARAMS (SIOCDEVPRIVATE+4) /* Set operational parameters. */
109 const char shortopts
[] = "aA:C:DfF:gG:hmM:p:rRsvVw?";
110 struct option longopts
[] = {
111 /* { name has_arg *flag val } */
112 {"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */
113 {"advertise", 1, 0, 'A'}, /* Change the capabilities advertised. */
114 {"BMCR", 1, 0, 'C'}, /* Set the control register. */
115 {"debug", 0, 0, 'D'}, /* Increase the debug level. */
116 {"force", 0, 0, 'f'}, /* Force the operation. */
117 {"fixed-speed", 1, 0, 'F'}, /* Fixed speed name. */
118 {"read-parameters", 0, 0, 'g'}, /* Show general settings values. */
119 {"set-parameters", 1, 0, 'G'}, /* Write general settings values. */
120 {"help", 0, 0, 'h'}, /* Print a long usage message. */
121 {"monitor", 0, 0, 'm'}, /* Monitor status register. */
122 {"msg-level", 1, 0, 'M'}, /* Set the driver message level. */
123 {"phy", 1, 0, 'p'}, /* Set the PHY (MII address) to report. */
124 {"restart", 0, 0, 'r'}, /* Restart the link negotiation */
125 {"reset", 0, 0, 'R'}, /* Reset the transceiver. */
126 {"status", 0, 0, 's'}, /* Non-zero exit status w/ no link beat. */
127 {"verbose", 0, 0, 'v'}, /* Report each action taken. */
128 {"version", 0, 0, 'V'}, /* Emit version information. */
129 {"watch", 0, 0, 'w'}, /* Constantly monitor the port. */
130 {"error", 0, 0, '?'}, /* Return the error message. */
134 /* Usually in libmii.c, but trivial substitions are below. */
135 extern int show_mii_details(long ioaddr
, int phy_id
);
136 extern void monitor_mii(long ioaddr
, int phy_id
);
137 int show_mii_details(long ioaddr
, int phy_id
) __attribute__((weak
));
138 void monitor_mii(long ioaddr
, int phy_id
) __attribute__((weak
));
141 /* Command-line flags. */
142 unsigned int opt_a
= 0, /* Show-all-interfaces flag. */
143 opt_f
= 0, /* Force the operation. */
146 verbose
= 0, /* Verbose flag. */
153 static int msg_level
= -1;
154 static int set_BMCR
= -1;
155 static int nway_advertise
= 0;
156 static int fixed_speed
= -1;
157 static int override_phy
= -1;
158 char *opt_G_string
= NULL
;
160 /* Internal values. */
162 int skfd
= -1; /* AF_INET socket for ioctl() calls. */
165 int do_one_xcvr(int skfd
);
166 int show_basic_mii(long ioaddr
, int phy_id
);
167 int mdio_read(int skfd
, int phy_id
, int location
);
168 void mdio_write(int skfd
, int phy_id
, int location
, int value
);
169 static int parse_advertise(const char *capabilities
);
170 static void monitor_status(long ioaddr
, int phy_id
);
174 main(int argc
, char **argv
)
178 char *progname
= rindex(argv
[0], '/') ? rindex(argv
[0], '/')+1 : argv
[0];
180 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, 0)) != EOF
)
182 case 'a': opt_a
++; break;
183 case 'A': nway_advertise
|= parse_advertise(optarg
);
184 if (nway_advertise
== -1) errflag
++;
186 case 'C': set_BMCR
= strtoul(optarg
, NULL
, 16); break;
187 case 'D': debug
++; break;
188 case 'f': opt_f
++; break;
189 case 'F': fixed_speed
= parse_advertise(optarg
);
190 if (fixed_speed
== -1) errflag
++;
192 case 'g': opt_g
++; break;
193 case 'G': opt_G
++; opt_G_string
= strdup(optarg
); break;
194 case 'm': opt_watch
++; opt_status
++; break;
195 case 'M': msg_level
= strtoul(optarg
, NULL
, 0); break;
196 case 'h': fprintf(stderr
, long_usage_msg
, progname
); return 0;
197 case 'p': override_phy
= atoi(optarg
); break;
198 case 'r': opt_restart
++; break;
199 case 'R': opt_reset
++; break;
200 case 's': opt_status
++; break;
201 case 'v': verbose
++; break;
202 case 'V': opt_version
++; break;
203 case 'w': opt_watch
++; break;
204 case '?': errflag
++; break;
207 fprintf(stderr
, usage_msg
, progname
);
211 if (verbose
|| opt_version
)
212 printf("%s", version
);
214 /* Open a basic socket. */
215 if ((skfd
= socket(AF_INET
, SOCK_DGRAM
,0)) < 0) {
221 fprintf(stderr
, "DEBUG: argc=%d, optind=%d and argv[optind] is %s.\n",
222 argc
, optind
, argv
[optind
]);
224 /* No remaining args means interface wasn't specified. */
225 if (optind
== argc
) {
226 fprintf(stderr
, "No interface specified.\n");
227 fprintf(stderr
, usage_msg
, progname
);
231 /* Copy the interface name. */
236 if (ifname
== NULL
) {
237 fprintf(stderr
, "No ifname.\n");
242 /* Verify that the interface supports the ioctl(), and if
243 it is using the new or old SIOCGMIIPHY value (grrr...).
246 u16
*data
= (u16
*)(&ifr
.ifr_data
);
248 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
249 ifr
.ifr_name
[IFNAMSIZ
-1] = '\0';
252 if (ioctl(skfd
, 0x8947, &ifr
) >= 0) {
254 } else if (ioctl(skfd
, SIOCDEVPRIVATE
, &ifr
) >= 0) {
257 fprintf(stderr
, "SIOCGMIIPHY on %s failed: %s\n", ifname
,
263 printf(" Using the %s SIOCGMIIPHY value on PHY %d "
265 new_ioctl_nums
? "new" : "old", data
[0], data
[3]);
274 int do_one_xcvr(int skfd
)
276 u16
*data
= (u16
*)(&ifr
.ifr_data
);
277 u32
*data32
= (u32
*)(&ifr
.ifr_data
);
278 unsigned phy_id
= data
[0];
280 if (override_phy
>= 0) {
281 printf("Using the specified MII PHY index %d.\n", override_phy
);
282 phy_id
= override_phy
;
285 if (opt_g
|| opt_G
|| msg_level
>= 0) {
286 if (ioctl(skfd
, SIOCGPARAMS
, &ifr
) < 0) {
287 fprintf(stderr
, "SIOCGPARAMS on %s failed: %s\n", ifr
.ifr_name
,
294 printf("Driver general parameter settings:");
295 for (i
= 0; i
*sizeof(u32
) < sizeof(ifr
.ifr_ifru
); i
++) {
296 printf(" %d", data32
[i
]);
301 /* Set up to four arbitrary driver parameters from the -G parameter.
302 The format is comma separated integers, with a missing element
303 retaining the previous value.
305 char *str
= opt_G_string
;
307 for (i
= 0; str
&& i
< 4; i
++) {
309 u32 newval
= strtol(str
, &endstr
, 0);
311 printf(" parse string '%s' value %d end '%s'.\n",
312 str
, newval
, endstr
);
314 if (endstr
[0] == ',') /* No parameter */
317 fprintf(stderr
, "Invalid driver parameter '%s'.\n", str
);
318 str
= index(str
, ',');
320 } else if (endstr
[0] == ',') {
323 } else if (endstr
[0] == 0) {
328 printf("Setting new driver general parameters:");
329 for (i
= 0; i
*sizeof(u32
) < sizeof(ifr
.ifr_ifru
); i
++) {
330 printf(" %d", data32
[i
]);
333 if (ioctl(skfd
, SIOCSPARAMS
, &ifr
) < 0) {
334 fprintf(stderr
, "SIOCSPARAMS on %s failed: %s\n", ifr
.ifr_name
,
339 if (msg_level
>= 0) {
340 data32
[0] = msg_level
;
341 if (ioctl(skfd
, SIOCSPARAMS
, &ifr
) < 0) {
342 fprintf(stderr
, "SIOCSPARAMS on %s failed: %s\n", ifr
.ifr_name
,
349 printf("Resetting the transceiver...\n");
350 mdio_write(skfd
, phy_id
, 0, 0x8000);
352 /* Note: PHY addresses > 32 are pseudo-MII devices, usually built-in. */
353 if (phy_id
< 64 && nway_advertise
> 0) {
354 printf(" Setting the media capability advertisement register of "
355 "PHY #%d to 0x%4.4x.\n", phy_id
, nway_advertise
| 1);
356 mdio_write(skfd
, phy_id
, 4, nway_advertise
| 1);
357 mdio_write(skfd
, phy_id
, 0, 0x1000);
361 printf("Restarting negotiation...\n");
362 mdio_write(skfd
, phy_id
, 0, 0x0000);
363 mdio_write(skfd
, phy_id
, 0, 0x1200);
365 /* To force 100baseTx-HD do mdio_write(skfd, phy_id, 0, 0x2000); */
366 if (fixed_speed
>= 0) {
368 if (fixed_speed
& 0x0180) /* 100mpbs */
370 if ((fixed_speed
& 0x0140) && /* A full duplex type and */
371 ! (fixed_speed
& 0x0820)) /* no half duplex types. */
373 printf("Setting the speed to \"fixed\", Control register %4.4x.\n",
375 mdio_write(skfd
, phy_id
, 0, reg0_val
);
378 printf("Setting the Basic Mode Control Register to 0x%4.4x.\n",
380 mdio_write(skfd
, phy_id
, 0, set_BMCR
);
383 if (opt_watch
&& opt_status
)
384 monitor_status(skfd
, phy_id
);
386 show_basic_mii(skfd
, phy_id
);
389 show_mii_details(skfd
, phy_id
);
391 if (verbose
|| debug
) {
392 int mii_reg
, mii_val
;
393 printf(" MII PHY #%d transceiver registers:", phy_id
);
394 for (mii_reg
= 0; mii_reg
< 32; mii_reg
++) {
395 mii_val
= mdio_read(skfd
, phy_id
, mii_reg
);
396 printf("%s %4.4x", (mii_reg
% 8) == 0 ? "\n " : "",
404 monitor_mii(skfd
, phy_id
);
406 (mdio_read(skfd
, phy_id
, 1) & 0x0004) == 0)
411 int mdio_read(int skfd
, int phy_id
, int location
)
413 u16
*data
= (u16
*)(&ifr
.ifr_data
);
418 if (ioctl(skfd
, new_ioctl_nums
? 0x8948 : SIOCDEVPRIVATE
+1, &ifr
) < 0) {
419 fprintf(stderr
, "SIOCGMIIREG on %s failed: %s\n", ifr
.ifr_name
,
426 void mdio_write(int skfd
, int phy_id
, int location
, int value
)
428 u16
*data
= (u16
*)(&ifr
.ifr_data
);
434 if (ioctl(skfd
, new_ioctl_nums
? 0x8949 : SIOCDEVPRIVATE
+2, &ifr
) < 0) {
435 fprintf(stderr
, "SIOCSMIIREG on %s failed: %s\n", ifr
.ifr_name
,
440 /* Parse the command line argument for advertised capabilities. */
441 static int parse_advertise(const char *capabilities
)
443 const char *mtypes
[] = {
444 "100baseT4", "100baseTx", "100baseTx-FD", "100baseTx-HD",
445 "10baseT", "10baseT-FD", "10baseT-HD", 0,
448 int cap_map
[] = { 0x0200, 0x0180, 0x0100, 0x0080, 0x0060, 0x0040, 0x0020,};
450 if ( ! capabilities
) {
451 fprintf(stderr
, "You passed -A 'NULL'. You must provide a media"
452 " list to advertise!\n");
456 fprintf(stderr
, "Advertise string is '%s'.\n", capabilities
);
457 for (i
= 0; mtypes
[i
]; i
++)
458 if (strcasecmp(mtypes
[i
], capabilities
) == 0)
460 if ((i
= strtol(capabilities
, &endptr
, 16)) <= 0xffff && endptr
[0] == 0)
462 fprintf(stderr
, "Invalid media advertisement value '%s'.\n"
463 " Either pass a numeric value or one of the following names:\n",
465 for (i
= 0; mtypes
[i
]; i
++)
466 fprintf(stderr
, " %-14s %3.3x\n", mtypes
[i
], cap_map
[i
]);
470 /* Trivial versions if we don't link against libmii.c */
471 static const char *media_names
[] = {
472 "10baseT", "10baseT-FD", "100baseTx", "100baseTx-FD", "100baseT4",
475 /* Various non-good bits in the command register. */
476 static const char *bmcr_bits
[] = {
477 " Internal Collision-Test enabled!\n", "", /* 0x0080,0x0100 */
478 " Restarted auto-negotiation in progress!\n",
479 " Transceiver isolated from the MII!\n",
480 " Transceiver powered down!\n", "", "",
481 " Transceiver in loopback mode!\n",
482 " Transceiver currently being reset!\n",
485 int show_basic_mii(long ioaddr
, int phy_id
)
489 u16 bmcr
, bmsr
, new_bmsr
, nway_advert
, lkpar
;
491 for (mii_reg
= 0; mii_reg
< 8; mii_reg
++)
492 mii_val
[mii_reg
] = mdio_read(ioaddr
, phy_id
, mii_reg
);
494 printf("Basic registers of MII PHY #%d: ", phy_id
);
495 for (mii_reg
= 0; mii_reg
< 8; mii_reg
++)
496 printf(" %4.4x", mii_val
[mii_reg
]);
500 if (mii_val
[0] == 0xffff || mii_val
[1] == 0x0000) {
501 printf(" No MII transceiver present!.\n");
503 printf(" Use '--force' to view the information anyway.\n");
507 /* Descriptive rename. */
510 nway_advert
= mii_val
[4];
513 if (lkpar
& 0x4000) {
514 int negotiated
= nway_advert
& lkpar
& 0x3e0;
515 int max_capability
= 0;
516 /* Scan for the highest negotiated capability, highest priority
517 (100baseTx-FDX) to lowest (10baseT-HDX). */
518 int media_priority
[] = {8, 9, 7, 6, 5}; /* media_names[i-5] */
519 printf(" The autonegotiated capability is %4.4x.\n", negotiated
);
520 for (i
= 0; media_priority
[i
]; i
++)
521 if (negotiated
& (1 << media_priority
[i
])) {
522 max_capability
= media_priority
[i
];
526 printf("The autonegotiated media type is %s.\n",
527 media_names
[max_capability
- 5]);
529 printf("No common media type was autonegotiated!\n"
530 "This is extremely unusual and typically indicates a "
531 "configuration error.\n" "Perhaps the advertised "
532 "capability set was intentionally limited.\n");
534 printf(" Basic mode control register 0x%4.4x:", bmcr
);
536 printf(" Auto-negotiation enabled.\n");
538 printf(" Auto-negotiation disabled, with\n"
539 " Speed fixed at 10%s mbps, %s-duplex.\n",
540 bmcr
& 0x2000 ? "0" : "",
541 bmcr
& 0x0100 ? "full":"half");
542 for (i
= 0; i
< 9; i
++)
543 if (bmcr
& (0x0080<<i
))
544 printf("%s", bmcr_bits
[i
]);
546 new_bmsr
= mdio_read(ioaddr
, phy_id
, 1);
547 if ((bmsr
& 0x0016) == 0x0004)
548 printf( " You have link beat, and everything is working OK.\n");
550 printf(" Basic mode status register 0x%4.4x ... %4.4x.\n"
551 " Link status: %sestablished.\n",
554 (new_bmsr
& 0x0004) ? "previously broken, but now re" : "not ");
556 printf(" This transceiver is capable of ");
558 for (i
= 15; i
>= 11; i
--)
560 printf(" %s", media_names
[i
-11]);
562 printf("<Warning! No media capabilities>");
564 printf(" %s to perform Auto-negotiation, negotiation %scomplete.\n",
565 bmsr
& 0x0008 ? "Able" : "Unable",
566 bmsr
& 0x0020 ? "" : "not ");
570 printf(" Remote fault detected!\n");
572 printf(" *** Link Jabber! ***\n");
574 if (lkpar
& 0x4000) {
575 printf(" Your link partner advertised %4.4x:",
577 for (i
= 5; i
>= 0; i
--)
578 if (lkpar
& (0x20<<i
))
579 printf(" %s", media_names
[i
]);
580 printf("%s.\n", lkpar
& 0x0400 ? ", w/ 802.3X flow control" : "");
581 } else if (lkpar
& 0x00A0)
582 printf(" Your link partner is generating %s link beat (no"
583 " autonegotiation).\n",
584 lkpar
& 0x0080 ? "100baseTx" : "10baseT");
585 else if ( ! (bmcr
& 0x1000))
586 printf(" Link partner information is not exchanged when in"
587 " fixed speed mode.\n");
588 else if ( ! (new_bmsr
& 0x004))
589 ; /* If no partner, do not report status. */
590 else if (lkpar
== 0x0001 || lkpar
== 0x0000) {
591 printf(" Your link partner does not do autonegotiation, and this "
592 "transceiver type\n does not report the sensed link "
595 printf(" Your link partner is strange, status %4.4x.\n", lkpar
);
597 printf(" End of basic transceiver information.\n\n");
601 static void monitor_status(long ioaddr
, int phy_id
)
603 unsigned int baseline_1
= 0x55555555; /* Always show initial status. */
606 unsigned int new_1
= mdio_read(ioaddr
, phy_id
, 1);
607 if (new_1
!= baseline_1
) {
608 printf("%-12s 0x%4.4x 0x%4.4x\n",
609 new_1
& 0x04 ? (new_1
==0xffff ? "unknown" : "up") :
610 new_1
& 0x20 ? "negotiating" : "down",
611 new_1
, mdio_read(ioaddr
, phy_id
, 5));
619 int show_mii_details(long ioaddr
, int phy_id
)
621 int mii_reg
, mii_val
;
622 printf(" MII PHY #%d transceiver registers:", phy_id
);
623 for (mii_reg
= 0; mii_reg
< 32; mii_reg
++) {
624 mii_val
= mdio_read(skfd
, phy_id
, mii_reg
);
625 printf("%s %4.4x", (mii_reg
% 8) == 0 ? "\n " : "",
628 printf("\nThis version of 'mii-diag' has not been linked with "
629 "the libmii.c library.\n"
630 " That library provides extended transceiver status reports.\n");
634 void monitor_mii(long ioaddr
, int phy_id
)
636 fprintf(stderr
, "\nThis version of 'mii-diag' has not been linked with "
637 "the libmii.c library \n"
638 " required for the media monitor option.\n");
646 * kept-new-versions: 5
650 * compile-command: "gcc -Wall -Wstrict-prototypes -O mii-diag.c -DLIBMII libmii.c -o mii-diag"
651 * simple-compile-command: "gcc mii-diag.c -o mii-diag"