]>
git.ipfire.org Git - 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 The default interface is \"eth0\".\n\
54 Frequently used options are\n\
55 -A --advertise <speed|setting>\n\
56 -F --fixed-speed <speed>\n\
57 Speed is one of: 100baseT4, 100baseTx, 100baseTx-FD, 100baseTx-HD,\n\
58 10baseT, 10baseT-FD, 10baseT-HD\n\
59 -s --status Return exit status 2 if there is no link beat.\n\
61 Less frequently used options are\n\
62 -a --all-interfaces Show the status all interfaces\n\
63 (Not recommended with options that change settings.)\n\
65 -g --read-parameters Get driver-specific parameters.\n\
66 -G --set-parameters PARMS Set driver-specific parameters.\n\
67 Parameters are comma separated, missing parameters retain\n\
68 their previous values.\n\
69 -M --msg-level LEVEL Set the driver message bit map.\n\
70 -p --phy ADDR Set the PHY (MII address) to report.\n\
71 -r --restart Restart the link autonegotiation.\n\
72 -R --reset Reset the transceiver.\n\
73 -v --verbose Report each action taken.\n\
74 -V --version Emit version information.\n\
75 -w --watch Continuously monitor the transceiver and report changes.\n\
77 This command returns success (zero) if the interface information can be\n\
78 read. If the --status option is passed, a zero return means that the\n\
79 interface has link beat.\n\
90 #include <sys/types.h>
91 #include <sys/socket.h>
92 #include <sys/ioctl.h>
94 #ifdef use_linux_libc5
95 #include <linux/if_arp.h>
96 #include <linux/if_ether.h>
99 typedef u_int32_t u32
;
100 typedef u_int16_t u16
;
103 #if defined(SIOCGPARAMS) && SIOCGPARAMS != SIOCDEVPRIVATE+3
104 #error Changed definition for SIOCGPARAMS
106 #define SIOCGPARAMS (SIOCDEVPRIVATE+3) /* Read operational parameters. */
107 #define SIOCSPARAMS (SIOCDEVPRIVATE+4) /* Set operational parameters. */
110 const char shortopts
[] = "aA:C:DfF:gG:hmM:p:rRsvVw?";
111 struct option longopts
[] = {
112 /* { name has_arg *flag val } */
113 {"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */
114 {"advertise", 1, 0, 'A'}, /* Change the capabilities advertised. */
115 {"BMCR", 1, 0, 'C'}, /* Set the control register. */
116 {"debug", 0, 0, 'D'}, /* Increase the debug level. */
117 {"force", 0, 0, 'f'}, /* Force the operation. */
118 {"fixed-speed", 1, 0, 'F'}, /* Fixed speed name. */
119 {"read-parameters", 0, 0, 'g'}, /* Show general settings values. */
120 {"set-parameters", 1, 0, 'G'}, /* Write general settings values. */
121 {"help", 0, 0, 'h'}, /* Print a long usage message. */
122 {"monitor", 0, 0, 'm'}, /* Monitor status register. */
123 {"msg-level", 1, 0, 'M'}, /* Set the driver message level. */
124 {"phy", 1, 0, 'p'}, /* Set the PHY (MII address) to report. */
125 {"restart", 0, 0, 'r'}, /* Restart the link negotiation */
126 {"reset", 0, 0, 'R'}, /* Reset the transceiver. */
127 {"status", 0, 0, 's'}, /* Non-zero exit status w/ no link beat. */
128 {"verbose", 0, 0, 'v'}, /* Report each action taken. */
129 {"version", 0, 0, 'V'}, /* Emit version information. */
130 {"watch", 0, 0, 'w'}, /* Constantly monitor the port. */
131 {"error", 0, 0, '?'}, /* Return the error message. */
135 /* Usually in libmii.c, but trivial substitions are below. */
136 extern int show_mii_details(long ioaddr
, int phy_id
);
137 extern void monitor_mii(long ioaddr
, int phy_id
);
138 int show_mii_details(long ioaddr
, int phy_id
) __attribute__((weak
));
139 void monitor_mii(long ioaddr
, int phy_id
) __attribute__((weak
));
142 /* Command-line flags. */
143 unsigned int opt_a
= 0, /* Show-all-interfaces flag. */
144 opt_f
= 0, /* Force the operation. */
147 verbose
= 0, /* Verbose flag. */
154 static int msg_level
= -1;
155 static int set_BMCR
= -1;
156 static int nway_advertise
= 0;
157 static int fixed_speed
= -1;
158 static int override_phy
= -1;
159 char *opt_G_string
= NULL
;
161 /* Internal values. */
163 int skfd
= -1; /* AF_INET socket for ioctl() calls. */
166 int do_one_xcvr(int skfd
);
167 int show_basic_mii(long ioaddr
, int phy_id
);
168 int mdio_read(int skfd
, int phy_id
, int location
);
169 void mdio_write(int skfd
, int phy_id
, int location
, int value
);
170 static int parse_advertise(const char *capabilities
);
171 static void monitor_status(long ioaddr
, int phy_id
);
175 main(int argc
, char **argv
)
179 char *progname
= rindex(argv
[0], '/') ? rindex(argv
[0], '/')+1 : argv
[0];
181 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, 0)) != EOF
)
183 case 'a': opt_a
++; break;
184 case 'A': nway_advertise
|= parse_advertise(optarg
);
185 if (nway_advertise
== -1) errflag
++;
187 case 'C': set_BMCR
= strtoul(optarg
, NULL
, 16); break;
188 case 'D': debug
++; break;
189 case 'f': opt_f
++; break;
190 case 'F': fixed_speed
= parse_advertise(optarg
);
191 if (fixed_speed
== -1) errflag
++;
193 case 'g': opt_g
++; break;
194 case 'G': opt_G
++; opt_G_string
= strdup(optarg
); break;
195 case 'm': opt_watch
++; opt_status
++; break;
196 case 'M': msg_level
= strtoul(optarg
, NULL
, 0); break;
197 case 'h': fprintf(stderr
, long_usage_msg
, progname
); return 0;
198 case 'p': override_phy
= atoi(optarg
); break;
199 case 'r': opt_restart
++; break;
200 case 'R': opt_reset
++; break;
201 case 's': opt_status
++; break;
202 case 'v': verbose
++; break;
203 case 'V': opt_version
++; break;
204 case 'w': opt_watch
++; break;
205 case '?': errflag
++; break;
208 fprintf(stderr
, usage_msg
, progname
);
212 if (verbose
|| opt_version
)
215 /* Open a basic socket. */
216 if ((skfd
= socket(AF_INET
, SOCK_DGRAM
,0)) < 0) {
222 fprintf(stderr
, "DEBUG: argc=%d, optind=%d and argv[optind] is %s.\n",
223 argc
, optind
, argv
[optind
]);
225 /* No remaining args means show all interfaces. */
226 if (optind
== argc
) {
228 fprintf(stderr
, "Using the default interface 'eth0'.\n");
230 /* Copy the interface name. */
235 if (ifname
== NULL
) {
237 fprintf(stderr
, "Using the default interface 'eth0'.\n");
240 /* Verify that the interface supports the ioctl(), and if
241 it is using the new or old SIOCGMIIPHY value (grrr...).
244 u16
*data
= (u16
*)(&ifr
.ifr_data
);
246 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
249 if (ioctl(skfd
, 0x8947, &ifr
) >= 0) {
251 } else if (ioctl(skfd
, SIOCDEVPRIVATE
, &ifr
) >= 0) {
254 fprintf(stderr
, "SIOCGMIIPHY on %s failed: %s\n", ifname
,
260 printf(" Using the %s SIOCGMIIPHY value on PHY %d "
262 new_ioctl_nums
? "new" : "old", data
[0], data
[3]);
271 int do_one_xcvr(int skfd
)
273 u16
*data
= (u16
*)(&ifr
.ifr_data
);
274 u32
*data32
= (u32
*)(&ifr
.ifr_data
);
275 unsigned phy_id
= data
[0];
277 if (override_phy
>= 0) {
278 printf("Using the specified MII PHY index %d.\n", override_phy
);
279 phy_id
= override_phy
;
282 if (opt_g
|| opt_G
|| msg_level
>= 0) {
283 if (ioctl(skfd
, SIOCGPARAMS
, &ifr
) < 0) {
284 fprintf(stderr
, "SIOCGPARAMS on %s failed: %s\n", ifr
.ifr_name
,
291 printf("Driver general parameter settings:");
292 for (i
= 0; i
*sizeof(u32
) < sizeof(ifr
.ifr_ifru
); i
++) {
293 printf(" %d", data32
[i
]);
298 /* Set up to four arbitrary driver parameters from the -G parameter.
299 The format is comma separated integers, with a missing element
300 retaining the previous value.
302 char *str
= opt_G_string
;
304 for (i
= 0; str
&& i
< 4; i
++) {
306 u32 newval
= strtol(str
, &endstr
, 0);
308 printf(" parse string '%s' value %d end '%s'.\n",
309 str
, newval
, endstr
);
311 if (endstr
[0] == ',') /* No parameter */
314 fprintf(stderr
, "Invalid driver parameter '%s'.\n", str
);
315 str
= index(str
, ',');
317 } else if (endstr
[0] == ',') {
320 } else if (endstr
[0] == 0) {
325 printf("Setting new driver general parameters:");
326 for (i
= 0; i
*sizeof(u32
) < sizeof(ifr
.ifr_ifru
); i
++) {
327 printf(" %d", data32
[i
]);
330 if (ioctl(skfd
, SIOCSPARAMS
, &ifr
) < 0) {
331 fprintf(stderr
, "SIOCSPARAMS on %s failed: %s\n", ifr
.ifr_name
,
336 if (msg_level
>= 0) {
337 data32
[0] = msg_level
;
338 if (ioctl(skfd
, SIOCSPARAMS
, &ifr
) < 0) {
339 fprintf(stderr
, "SIOCSPARAMS on %s failed: %s\n", ifr
.ifr_name
,
346 printf("Resetting the transceiver...\n");
347 mdio_write(skfd
, phy_id
, 0, 0x8000);
349 /* Note: PHY addresses > 32 are pseudo-MII devices, usually built-in. */
350 if (phy_id
< 64 && nway_advertise
> 0) {
351 printf(" Setting the media capability advertisement register of "
352 "PHY #%d to 0x%4.4x.\n", phy_id
, nway_advertise
| 1);
353 mdio_write(skfd
, phy_id
, 4, nway_advertise
| 1);
354 mdio_write(skfd
, phy_id
, 0, 0x1000);
358 printf("Restarting negotiation...\n");
359 mdio_write(skfd
, phy_id
, 0, 0x0000);
360 mdio_write(skfd
, phy_id
, 0, 0x1200);
362 /* To force 100baseTx-HD do mdio_write(skfd, phy_id, 0, 0x2000); */
363 if (fixed_speed
>= 0) {
365 if (fixed_speed
& 0x0180) /* 100mpbs */
367 if ((fixed_speed
& 0x0140) && /* A full duplex type and */
368 ! (fixed_speed
& 0x0820)) /* no half duplex types. */
370 printf("Setting the speed to \"fixed\", Control register %4.4x.\n",
372 mdio_write(skfd
, phy_id
, 0, reg0_val
);
375 printf("Setting the Basic Mode Control Register to 0x%4.4x.\n",
377 mdio_write(skfd
, phy_id
, 0, set_BMCR
);
380 if (opt_watch
&& opt_status
)
381 monitor_status(skfd
, phy_id
);
383 show_basic_mii(skfd
, phy_id
);
386 show_mii_details(skfd
, phy_id
);
388 if (verbose
|| debug
) {
389 int mii_reg
, mii_val
;
390 printf(" MII PHY #%d transceiver registers:", phy_id
);
391 for (mii_reg
= 0; mii_reg
< 32; mii_reg
++) {
392 mii_val
= mdio_read(skfd
, phy_id
, mii_reg
);
393 printf("%s %4.4x", (mii_reg
% 8) == 0 ? "\n " : "",
401 monitor_mii(skfd
, phy_id
);
403 (mdio_read(skfd
, phy_id
, 1) & 0x0004) == 0)
408 int mdio_read(int skfd
, int phy_id
, int location
)
410 u16
*data
= (u16
*)(&ifr
.ifr_data
);
415 if (ioctl(skfd
, new_ioctl_nums
? 0x8948 : SIOCDEVPRIVATE
+1, &ifr
) < 0) {
416 fprintf(stderr
, "SIOCGMIIREG on %s failed: %s\n", ifr
.ifr_name
,
423 void mdio_write(int skfd
, int phy_id
, int location
, int value
)
425 u16
*data
= (u16
*)(&ifr
.ifr_data
);
431 if (ioctl(skfd
, new_ioctl_nums
? 0x8949 : SIOCDEVPRIVATE
+2, &ifr
) < 0) {
432 fprintf(stderr
, "SIOCSMIIREG on %s failed: %s\n", ifr
.ifr_name
,
437 /* Parse the command line argument for advertised capabilities. */
438 static int parse_advertise(const char *capabilities
)
440 const char *mtypes
[] = {
441 "100baseT4", "100baseTx", "100baseTx-FD", "100baseTx-HD",
442 "10baseT", "10baseT-FD", "10baseT-HD", 0,
445 int cap_map
[] = { 0x0200, 0x0180, 0x0100, 0x0080, 0x0060, 0x0040, 0x0020,};
447 if ( ! capabilities
) {
448 fprintf(stderr
, "You passed -A 'NULL'. You must provide a media"
449 " list to advertise!\n");
453 fprintf(stderr
, "Advertise string is '%s'.\n", capabilities
);
454 for (i
= 0; mtypes
[i
]; i
++)
455 if (strcasecmp(mtypes
[i
], capabilities
) == 0)
457 if ((i
= strtol(capabilities
, &endptr
, 16)) <= 0xffff && endptr
[0] == 0)
459 fprintf(stderr
, "Invalid media advertisement value '%s'.\n"
460 " Either pass a numeric value or one of the following names:\n",
462 for (i
= 0; mtypes
[i
]; i
++)
463 fprintf(stderr
, " %-14s %3.3x\n", mtypes
[i
], cap_map
[i
]);
467 /* Trivial versions if we don't link against libmii.c */
468 static const char *media_names
[] = {
469 "10baseT", "10baseT-FD", "100baseTx", "100baseTx-FD", "100baseT4",
472 /* Various non-good bits in the command register. */
473 static const char *bmcr_bits
[] = {
474 " Internal Collision-Test enabled!\n", "", /* 0x0080,0x0100 */
475 " Restarted auto-negotiation in progress!\n",
476 " Transceiver isolated from the MII!\n",
477 " Transceiver powered down!\n", "", "",
478 " Transceiver in loopback mode!\n",
479 " Transceiver currently being reset!\n",
482 int show_basic_mii(long ioaddr
, int phy_id
)
486 u16 bmcr
, bmsr
, new_bmsr
, nway_advert
, lkpar
;
488 for (mii_reg
= 0; mii_reg
< 8; mii_reg
++)
489 mii_val
[mii_reg
] = mdio_read(ioaddr
, phy_id
, mii_reg
);
491 printf("Basic registers of MII PHY #%d: ", phy_id
);
492 for (mii_reg
= 0; mii_reg
< 8; mii_reg
++)
493 printf(" %4.4x", mii_val
[mii_reg
]);
497 if (mii_val
[0] == 0xffff || mii_val
[1] == 0x0000) {
498 printf(" No MII transceiver present!.\n");
500 printf(" Use '--force' to view the information anyway.\n");
504 /* Descriptive rename. */
507 nway_advert
= mii_val
[4];
510 if (lkpar
& 0x4000) {
511 int negotiated
= nway_advert
& lkpar
& 0x3e0;
512 int max_capability
= 0;
513 /* Scan for the highest negotiated capability, highest priority
514 (100baseTx-FDX) to lowest (10baseT-HDX). */
515 int media_priority
[] = {8, 9, 7, 6, 5}; /* media_names[i-5] */
516 printf(" The autonegotiated capability is %4.4x.\n", negotiated
);
517 for (i
= 0; media_priority
[i
]; i
++)
518 if (negotiated
& (1 << media_priority
[i
])) {
519 max_capability
= media_priority
[i
];
523 printf("The autonegotiated media type is %s.\n",
524 media_names
[max_capability
- 5]);
526 printf("No common media type was autonegotiated!\n"
527 "This is extremely unusual and typically indicates a "
528 "configuration error.\n" "Perhaps the advertised "
529 "capability set was intentionally limited.\n");
531 printf(" Basic mode control register 0x%4.4x:", bmcr
);
533 printf(" Auto-negotiation enabled.\n");
535 printf(" Auto-negotiation disabled, with\n"
536 " Speed fixed at 10%s mbps, %s-duplex.\n",
537 bmcr
& 0x2000 ? "0" : "",
538 bmcr
& 0x0100 ? "full":"half");
539 for (i
= 0; i
< 9; i
++)
540 if (bmcr
& (0x0080<<i
))
541 printf(bmcr_bits
[i
]);
543 new_bmsr
= mdio_read(ioaddr
, phy_id
, 1);
544 if ((bmsr
& 0x0016) == 0x0004)
545 printf( " You have link beat, and everything is working OK.\n");
547 printf(" Basic mode status register 0x%4.4x ... %4.4x.\n"
548 " Link status: %sestablished.\n",
551 (new_bmsr
& 0x0004) ? "previously broken, but now re" : "not ");
553 printf(" This transceiver is capable of ");
555 for (i
= 15; i
>= 11; i
--)
557 printf(" %s", media_names
[i
-11]);
559 printf("<Warning! No media capabilities>");
561 printf(" %s to perform Auto-negotiation, negotiation %scomplete.\n",
562 bmsr
& 0x0008 ? "Able" : "Unable",
563 bmsr
& 0x0020 ? "" : "not ");
567 printf(" Remote fault detected!\n");
569 printf(" *** Link Jabber! ***\n");
571 if (lkpar
& 0x4000) {
572 printf(" Your link partner advertised %4.4x:",
574 for (i
= 5; i
>= 0; i
--)
575 if (lkpar
& (0x20<<i
))
576 printf(" %s", media_names
[i
]);
577 printf("%s.\n", lkpar
& 0x0400 ? ", w/ 802.3X flow control" : "");
578 } else if (lkpar
& 0x00A0)
579 printf(" Your link partner is generating %s link beat (no"
580 " autonegotiation).\n",
581 lkpar
& 0x0080 ? "100baseTx" : "10baseT");
582 else if ( ! (bmcr
& 0x1000))
583 printf(" Link partner information is not exchanged when in"
584 " fixed speed mode.\n");
585 else if ( ! (new_bmsr
& 0x004))
586 ; /* If no partner, do not report status. */
587 else if (lkpar
== 0x0001 || lkpar
== 0x0000) {
588 printf(" Your link partner does not do autonegotiation, and this "
589 "transceiver type\n does not report the sensed link "
592 printf(" Your link partner is strange, status %4.4x.\n", lkpar
);
594 printf(" End of basic transceiver information.\n\n");
598 static void monitor_status(long ioaddr
, int phy_id
)
600 unsigned int baseline_1
= 0x55555555; /* Always show initial status. */
603 unsigned int new_1
= mdio_read(ioaddr
, phy_id
, 1);
604 if (new_1
!= baseline_1
) {
605 printf("%-12s 0x%4.4x 0x%4.4x\n",
606 new_1
& 0x04 ? (new_1
==0xffff ? "unknown" : "up") :
607 new_1
& 0x20 ? "negotiating" : "down",
608 new_1
, mdio_read(ioaddr
, phy_id
, 5));
616 int show_mii_details(long ioaddr
, int phy_id
)
618 int mii_reg
, mii_val
;
619 printf(" MII PHY #%d transceiver registers:", phy_id
);
620 for (mii_reg
= 0; mii_reg
< 32; mii_reg
++) {
621 mii_val
= mdio_read(skfd
, phy_id
, mii_reg
);
622 printf("%s %4.4x", (mii_reg
% 8) == 0 ? "\n " : "",
625 printf("\nThis version of 'mii-diag' has not been linked with "
626 "the libmii.c library.\n"
627 " That library provides extended transceiver status reports.\n");
631 void monitor_mii(long ioaddr
, int phy_id
)
633 fprintf(stderr
, "\nThis version of 'mii-diag' has not been linked with "
634 "the libmii.c library \n"
635 " required for the media monitor option.\n");
643 * kept-new-versions: 5
647 * compile-command: "gcc -Wall -Wstrict-prototypes -O mii-diag.c -DLIBMII libmii.c -o mii-diag"
648 * simple-compile-command: "gcc mii-diag.c -o mii-diag"