3 mii-tool: monitor and control the MII for a network interface
7 mii-tool [-VvRrw] [-A media,... | -F media] [interface ...]
9 This program is based on Donald Becker's "mii-diag" program, which
10 is more capable and verbose than this tool, but also somewhat
13 Copyright (C) 2000 David A. Hinds -- dhinds@pcmcia.sourceforge.org
15 mii-diag is written/copyright 1997-2000 by Donald Becker
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 Donald Becker may be reached as becker@scyld.com, or C/O
23 Scyld Computing Corporation, 410 Severn Av., Suite 210,
27 http://www.scyld.com/diag/mii-status.html
28 http://www.scyld.com/expert/NWay.html
29 http://www.national.com/pf/DP/DP83840.html
32 static char version
[] =
33 "mii-tool.c 1.9.1 2000/04/28 00:56:08 (David Hinds)\n";
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/ioctl.h>
49 #include <linux/sockios.h>
52 #include <linux/if_arp.h>
53 #include <linux/if_ether.h>
57 #define MAX_ETH 8 /* Maximum # of interfaces */
59 /* Table of known MII's */
64 { 0x0022, 0x5610, "AdHoc AH101LF" },
65 { 0x0022, 0x5520, "Altimata AC101LF" },
66 { 0x0000, 0x6b90, "AMD 79C901A HomePNA" },
67 { 0x0000, 0x6b70, "AMD 79C901A 10baseT" },
68 { 0x0181, 0xb800, "Davicom DM9101" },
69 { 0x0043, 0x7411, "Enable EL40-331" },
70 { 0x0015, 0xf410, "ICS 1889" },
71 { 0x0015, 0xf420, "ICS 1890" },
72 { 0x0015, 0xf430, "ICS 1892" },
73 { 0x02a8, 0x0150, "Intel 82555" },
74 { 0x7810, 0x0000, "Level One LXT970/971" },
75 { 0x2000, 0x5c00, "National DP83840A" },
76 { 0x0181, 0x4410, "Quality QS6612" },
77 { 0x0282, 0x1c50, "SMSC 83C180" },
78 { 0x0300, 0xe540, "TDK 78Q2120" },
79 { 0x0141, 0x0c20, "Yukon 88E1011" },
80 { 0x0141, 0x0cc0, "Yukon-EC 88E1111" },
81 { 0x0141, 0x0c90, "Yukon-2 88E1112" },
83 #define NMII (sizeof(mii_id)/sizeof(mii_id[0]))
85 /*--------------------------------------------------------------------*/
87 struct option longopts
[] = {
88 /* { name has_arg *flag val } */
89 {"advertise", 1, 0, 'A'}, /* Change capabilities advertised. */
90 {"force", 1, 0, 'F'}, /* Change capabilities advertised. */
91 {"phy", 1, 0, 'p'}, /* Set PHY (MII address) to report. */
92 {"log", 0, 0, 'l'}, /* Write events to syslog. */
93 {"restart", 0, 0, 'r'}, /* Restart link negotiation */
94 {"reset", 0, 0, 'R'}, /* Reset the transceiver. */
95 {"verbose", 0, 0, 'v'}, /* Report each action taken. */
96 {"version", 0, 0, 'V'}, /* Emit version information. */
97 {"watch", 0, 0, 'w'}, /* Constantly monitor the port. */
98 {"help", 0, 0, '?'}, /* Give help */
109 static u_long nway_advertise
= 0;
110 static u_long fixed_speed
= 0;
111 static int override_phy
= -1;
113 static struct ifreq ifr
;
114 static int supports_gmii
;
116 /*--------------------------------------------------------------------*/
118 static int mdio_read(int skfd
, int location
)
120 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
122 mii
->reg_num
= location
;
123 if (ioctl(skfd
, SIOCGMIIREG
, &ifr
) < 0) {
124 fprintf(stderr
, "SIOCGMIIREG on %s failed: %s\n", ifr
.ifr_name
,
131 static void mdio_write(int skfd
, int location
, int value
)
133 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
135 mii
->reg_num
= location
;
137 if (ioctl(skfd
, SIOCSMIIREG
, &ifr
) < 0) {
138 fprintf(stderr
, "SIOCSMIIREG on %s failed: %s\n", ifr
.ifr_name
,
143 /*--------------------------------------------------------------------*/
149 /* The order through 100baseT4 matches bits in the BMSR */
150 { "10baseT-HD", {MII_AN_10BASET_HD
, 0} },
151 { "10baseT-FD", {MII_AN_10BASET_FD
, 0} },
152 { "100baseTx-HD", {MII_AN_100BASETX_HD
, 0} },
153 { "100baseTx-FD", {MII_AN_100BASETX_FD
, 0} },
154 { "100baseT4", {MII_AN_100BASET4
, 0} },
155 { "100baseTx", {MII_AN_100BASETX_FD
| MII_AN_100BASETX_HD
, 0} },
156 { "10baseT", {MII_AN_10BASET_FD
| MII_AN_10BASET_HD
, 0} },
158 { "1000baseT-HD", {0, MII_AN2_1000HALF
} },
159 { "1000baseT-FD", {0, MII_AN2_1000FULL
} },
160 { "1000baseT", {0, MII_AN2_1000HALF
|MII_AN2_1000FULL
} },
162 #define NMEDIA (sizeof(media)/sizeof(media[0]))
164 /* Parse an argument list of media types */
165 static u_long
parse_media(char *arg
)
171 mask
= strtoul(arg
, &s
, 16);
172 if ((*arg
!= '\0') && (*s
== '\0')) {
173 if ((mask
& MII_AN_ABILITY_MASK
) &&
174 !(mask
& ~MII_AN_ABILITY_MASK
)) {
180 s
= strtok(arg
, ", ");
182 for (i
= 0; i
< NMEDIA
; i
++)
183 if (strcasecmp(media
[i
].name
, s
) == 0) break;
184 if (i
== NMEDIA
) goto failed
;
185 mask
|= media
[i
].value
[0];
186 mask
|= media
[i
].value
[1] << 16;
187 } while ((s
= strtok(NULL
, ", ")) != NULL
);
191 fprintf(stderr
, "Invalid media specification '%s'.\n", arg
);
195 /*--------------------------------------------------------------------*/
197 static const char *media_list(unsigned mask
, unsigned mask2
, int best
)
199 static char buf
[100];
204 for (i
= 8; i
>= 7; i
--) {
205 if (mask2
& media
[i
].value
[1]) {
207 strcat(buf
, media
[i
].name
);
214 for (i
= 4; i
>= 0; i
--) {
217 strcat(buf
, media
[i
].name
);
223 strcat(buf
, " flow-control");
227 static int show_basic_mii(int sock
, int phy_id
)
231 u_short mii_val
[MII_MAXPHYREG
];
232 unsigned bmcr
, bmsr
, advert
, lkpar
, bmcr2
, bmsr2
, lpa2
;
234 /* Some bits in the BMSR are latched, but we can't rely on being
235 the only reader, so only the current values are meaningful */
236 mdio_read(sock
, MII_BMSR
);
237 for (i
= 0; i
< ((verbose
> 1) ? MII_MAXPHYREG
: MII_BASIC_MAX
+1); i
++)
238 mii_val
[i
] = mdio_read(sock
, i
);
240 if (mii_val
[MII_BMCR
] == 0xffff) {
241 fprintf(stderr
, " No MII transceiver present!.\n");
245 /* Descriptive rename. */
246 bmcr
= mii_val
[MII_BMCR
]; bmsr
= mii_val
[MII_BMSR
];
247 advert
= mii_val
[MII_ANAR
]; lkpar
= mii_val
[MII_ANLPAR
];
248 /* Gigabit registers */
249 bmcr2
= mii_val
[MII_CTRL1000
]; lpa2
= mii_val
[MII_STAT1000
];
250 bmsr2
= mii_val
[MII_ESTAT1000
];
252 sprintf(buf
, "%s: ", ifr
.ifr_name
);
253 if (bmcr
& MII_BMCR_AN_ENA
) {
254 if (bmsr
& MII_BMSR_AN_COMPLETE
) {
255 if (advert
& lkpar
) {
256 strcat(buf
, (lkpar
& MII_AN_ACK
) ?
257 "negotiated" : "no autonegotiation,");
258 strcat(buf
, media_list(advert
& lkpar
, bmcr2
& (lpa2
>>2), 1));
261 strcat(buf
, "autonegotiation failed, ");
263 } else if (bmcr
& MII_BMCR_RESTART
) {
264 strcat(buf
, "autonegotiation restarted, ");
267 sprintf(buf
+strlen(buf
), "%s Mbit, %s duplex, ",
268 (bmcr
& MII_BMCR_1000MBIT
) ? "1000"
269 : (bmcr
& MII_BMCR_100MBIT
) ? "100" : "10",
270 (bmcr
& MII_BMCR_DUPLEX
) ? "full" : "half");
272 strcat(buf
, (bmsr
& MII_BMSR_LINK_VALID
) ? "link ok" : "no link");
276 syslog(LOG_INFO
, buf
);
279 time_t t
= time(NULL
);
280 strftime(s
, sizeof(s
), "%T", localtime(&t
));
281 printf("%s %s\n", s
, buf
);
288 printf(" registers for MII PHY %d: ", phy_id
);
289 for (i
= 0; i
< MII_MAXPHYREG
; i
++)
290 printf("%s %4.4x", ((i
% 8) ? "" : "\n "), mii_val
[i
]);
295 printf(" product info: ");
296 for (i
= 0; i
< NMII
; i
++)
297 if ((mii_id
[i
].id1
== mii_val
[MII_PHY_ID1
]) &&
298 (mii_id
[i
].id2
== (mii_val
[MII_PHY_ID2
] & 0xfff0)))
301 printf("%s rev %d\n", mii_id
[i
].name
, mii_val
[MII_PHY_ID2
]&0x0f);
303 printf("vendor %02x:%02x:%02x, model %d rev %d\n",
304 mii_val
[MII_PHY_ID1
]>>10, (mii_val
[MII_PHY_ID1
]>>2)&0xff,
305 ((mii_val
[MII_PHY_ID1
]<<6)|(mii_val
[MII_PHY_ID2
]>>10))&0xff,
306 (mii_val
[MII_PHY_ID2
]>>4)&0x3f, mii_val
[MII_PHY_ID2
]&0x0f);
307 printf(" basic mode: ");
308 if (bmcr
& MII_BMCR_RESET
)
309 printf("software reset, ");
310 if (bmcr
& MII_BMCR_LOOPBACK
)
311 printf("loopback, ");
312 if (bmcr
& MII_BMCR_ISOLATE
)
314 if (bmcr
& MII_BMCR_COLTEST
)
315 printf("collision test, ");
316 if (bmcr
& MII_BMCR_AN_ENA
) {
317 printf("autonegotiation enabled\n");
319 printf("%s Mbit, %s duplex\n",
320 (bmcr
& MII_BMCR_1000MBIT
) ? "1000"
321 : (bmcr
& MII_BMCR_100MBIT
) ? "100" : "10",
322 (bmcr
& MII_BMCR_DUPLEX
) ? "full" : "half");
324 printf(" basic status: ");
325 if (bmsr
& MII_BMSR_AN_COMPLETE
)
326 printf("autonegotiation complete, ");
327 else if (bmcr
& MII_BMCR_RESTART
)
328 printf("autonegotiation restarted, ");
329 if (bmsr
& MII_BMSR_REMOTE_FAULT
)
330 printf("remote fault, ");
331 printf((bmsr
& MII_BMSR_LINK_VALID
) ? "link ok" : "no link");
332 printf("\n capabilities:%s", media_list(bmsr
>> 6, bmsr2
>> 4, 0));
333 printf("\n advertising: %s", media_list(advert
, bmcr2
, 0));
334 if (lkpar
& MII_AN_ABILITY_MASK
)
335 printf("\n link partner:%s", media_list(lkpar
, lpa2
>> 2, 0));
341 /*--------------------------------------------------------------------*/
343 static inline int ifr_prep(int skfd
, char *ifname
, int maybe
)
345 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
347 /* Get the vitals from the interface. */
348 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
349 if (ioctl(skfd
, SIOCGMIIPHY
, &ifr
) < 0) {
350 if (!maybe
|| (errno
!= ENODEV
))
351 fprintf(stderr
, "SIOCGMIIPHY on '%s' failed: %s\n",
352 ifname
, strerror(errno
));
356 if (override_phy
>= 0) {
357 printf("using the specified MII index %d.\n", override_phy
);
358 mii
->phy_id
= override_phy
;
359 } else if (mii
->phy_id
== EPHY_NOREG
) {
364 if (mdio_read(skfd
, MII_BMSR
) & MII_BMSR_ESTATEN
)
365 supports_gmii
= mdio_read(skfd
, MII_ESTAT1000
) & 0xf000;
370 /*--------------------------------------------------------------------*/
372 static int do_one_xcvr(int skfd
, char *ifname
, int maybe
)
374 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
376 if (ifr_prep(skfd
, ifname
, maybe
))
380 printf("resetting the transceiver...\n");
381 mdio_write(skfd
, MII_BMCR
, MII_BMCR_RESET
);
383 if (nway_advertise
) {
385 int bmcr2
= mdio_read(skfd
, MII_CTRL1000
);
386 bmcr2
&= ~(MII_AN2_1000HALF
|MII_AN2_1000FULL
);
387 bmcr2
|= (nway_advertise
>> 16) & (MII_AN2_1000HALF
|MII_AN2_1000FULL
);
388 mdio_write(skfd
, MII_CTRL1000
, bmcr2
);
390 mdio_write(skfd
, MII_ANAR
, (nway_advertise
| 1) & 0xffff);
394 printf("restarting autonegotiation...\n");
395 mdio_write(skfd
, MII_BMCR
, 0x0000);
396 mdio_write(skfd
, MII_BMCR
, MII_BMCR_AN_ENA
|MII_BMCR_RESTART
);
400 if (fixed_speed
& ((MII_AN2_1000HALF
<<16)|(MII_AN2_1000FULL
<<16)))
401 bmcr
|= MII_BMCR_1000MBIT
;
402 if (fixed_speed
& (MII_AN_100BASETX_FD
|MII_AN_100BASETX_HD
))
403 bmcr
|= MII_BMCR_100MBIT
;
404 if (fixed_speed
& (MII_AN_100BASETX_FD
|MII_AN_10BASET_FD
|(MII_AN2_1000FULL
<<16)))
405 bmcr
|= MII_BMCR_DUPLEX
;
406 mdio_write(skfd
, MII_BMCR
, bmcr
);
409 if (!opt_restart
&& !opt_reset
&& !fixed_speed
&& !nway_advertise
)
410 show_basic_mii(skfd
, mii
->phy_id
);
415 /*--------------------------------------------------------------------*/
417 static void watch_one_xcvr(int skfd
, char *ifname
, int index
)
419 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
420 static int status
[MAX_ETH
] = { 0, /* ... */ };
423 if (ifr_prep(skfd
, ifname
, 0))
426 now
= (mdio_read(skfd
, MII_BMCR
) |
427 (mdio_read(skfd
, MII_BMSR
) << 16));
428 if (status
[index
] && (status
[index
] != now
))
429 show_basic_mii(skfd
, mii
->phy_id
);
433 /*--------------------------------------------------------------------*/
436 "usage: %s [-VvRrwl] [-A media,... | -F media] [-p port] [interface ...]\n"
437 " -V, --version display version information\n"
438 " -v, --verbose more verbose output\n"
439 " -R, --reset reset MII to poweron state\n"
440 " -r, --restart restart autonegotiation\n"
441 " -w, --watch monitor for link status changes\n"
442 " -l, --log with -w, write events to syslog\n"
443 " -A, --advertise=media,... advertise only specified media\n"
444 " -F, --force=media force specified media technology\n"
445 " -p, --phy=port override PHY port number\n"
446 "media: 1000baseT-FD, 1000baseT-HD, 100baseT4, 100baseTx-FD, 100baseTx-HD,\n"
447 " 10baseT-FD, 10baseT-HD,\n"
448 " (to advertise both HD and FD) 1000baseT, 100baseTx, 10baseT\n";
450 int main(int argc
, char **argv
)
452 int skfd
; /* AF_INET socket for ioctl() calls. */
453 int i
, c
, ret
, helpflag
= 0;
456 while ((c
= getopt_long(argc
, argv
, "A:F:p:lrRvVw?", longopts
, 0)) != EOF
)
458 case 'A': nway_advertise
= parse_media(optarg
); break;
459 case 'F': fixed_speed
= parse_media(optarg
); break;
460 case 'p': override_phy
= atoi(optarg
); break;
461 case 'r': opt_restart
++; break;
462 case 'R': opt_reset
++; break;
463 case 'v': verbose
++; break;
464 case 'V': opt_version
++; break;
465 case 'w': opt_watch
++; break;
466 case 'l': opt_log
++; break;
467 case '?': helpflag
++; break;
469 /* Check for a few inappropriate option combinations */
470 if (opt_watch
) verbose
= 0;
471 if (helpflag
|| (fixed_speed
& (fixed_speed
-1)) ||
472 (fixed_speed
&& (opt_restart
|| nway_advertise
))) {
473 fprintf(stderr
, usage
, argv
[0]);
480 /* Open a basic socket. */
481 if ((skfd
= socket(AF_INET
, SOCK_DGRAM
,0)) < 0) {
486 /* No remaining args means show all interfaces. */
487 if (optind
== argc
) {
489 for (i
= 0; i
< MAX_ETH
; i
++) {
490 sprintf(s
, "eth%d", i
);
491 ret
&= do_one_xcvr(skfd
, s
, 1);
494 fprintf(stderr
, "no MII interfaces found\n");
497 for (i
= optind
; i
< argc
; i
++) {
498 ret
|= do_one_xcvr(skfd
, argv
[i
], 0);
502 if (opt_watch
&& (ret
== 0)) {
505 if (optind
== argc
) {
506 for (i
= 0; i
< MAX_ETH
; i
++) {
507 sprintf(s
, "eth%d", i
);
508 watch_one_xcvr(skfd
, s
, i
);
511 for (i
= optind
; i
< argc
; i
++)
512 watch_one_xcvr(skfd
, argv
[i
], i
-optind
);