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[] = "$Id: mii-tool.c,v 1.9 2006/09/27 20:59:18 ecki Exp $\n(Author: David Hinds based on Donald Becker's mii-diag)";
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/ioctl.h>
48 #include <linux/sockios.h>
51 #include <linux/if_arp.h>
52 #include <linux/if_ether.h>
54 #include <linux/mii.h>
55 #include <linux/sockios.h>
58 #define MAX_ETH 8 /* Maximum # of interfaces */
59 #define LPA_ABILITY_MASK 0x07e0
61 /* Table of known MII's */
66 { 0x0022, 0x5610, "AdHoc AH101LF" },
67 { 0x0022, 0x5520, "Altimata AC101LF" },
68 { 0x0000, 0x6b90, "AMD 79C901A HomePNA" },
69 { 0x0000, 0x6b70, "AMD 79C901A 10baseT" },
70 { 0x0181, 0xb800, "Davicom DM9101" },
71 { 0x0043, 0x7411, "Enable EL40-331" },
72 { 0x0015, 0xf410, "ICS 1889" },
73 { 0x0015, 0xf420, "ICS 1890" },
74 { 0x0015, 0xf430, "ICS 1892" },
75 { 0x02a8, 0x0150, "Intel 82555" },
76 { 0x7810, 0x0000, "Level One LXT970/971" },
77 { 0x2000, 0x5c00, "National DP83840A" },
78 { 0x0181, 0x4410, "Quality QS6612" },
79 { 0x0282, 0x1c50, "SMSC 83C180" },
80 { 0x0300, 0xe540, "TDK 78Q2120" },
81 { 0x0141, 0x0c20, "Yukon 88E1011" },
82 { 0x0141, 0x0cc0, "Yukon-EC 88E1111" },
83 { 0x0141, 0x0c90, "Yukon-2 88E1112" },
85 #define NMII (sizeof(mii_id)/sizeof(mii_id[0]))
87 /*--------------------------------------------------------------------*/
89 struct option longopts[] = {
90 /* { name has_arg *flag val } */
91 {"advertise", 1, 0, 'A'}, /* Change capabilities advertised. */
92 {"force", 1, 0, 'F'}, /* Change capabilities advertised. */
93 {"phy", 1, 0, 'p'}, /* Set PHY (MII address) to report. */
94 {"log", 0, 0, 'l'}, /* Set PHY (MII address) to report. */
95 {"restart", 0, 0, 'r'}, /* Restart link negotiation */
96 {"reset", 0, 0, 'R'}, /* Reset the transceiver. */
97 {"verbose", 0, 0, 'v'}, /* Report each action taken. */
98 {"version", 0, 0, 'V'}, /* Emit version information. */
99 {"watch", 0, 0, 'w'}, /* Constantly monitor the port. */
100 {"help", 0, 0, '?'}, /* Give help */
111 static int nway_advertise = 0;
112 static int fixed_speed = 0;
113 static int override_phy = -1;
115 static int skfd = -1; /* AF_INET socket for ioctl() calls. */
116 static struct ifreq ifr;
118 /*--------------------------------------------------------------------*/
120 static int mdio_read(int skfd, int location)
122 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
123 mii->reg_num = location;
124 if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) {
125 fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,
132 static void mdio_write(int skfd, int location, int value)
134 struct mii_ioctl_data *mii = (struct mii_ioctl_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", {LPA_10HALF} },
151 { "10baseT-FD", {LPA_10FULL} },
152 { "100baseTx-HD", {LPA_100HALF} },
153 { "100baseTx-FD", {LPA_100FULL} },
154 { "100baseT4", {LPA_100BASE4} },
155 { "100baseTx", {LPA_100FULL | LPA_100HALF} },
156 { "10baseT", {LPA_10FULL | LPA_10HALF} },
158 { "1000baseT-HD", {0, ADVERTISE_1000HALF} },
159 { "1000baseT-FD", {0, ADVERTISE_1000FULL} },
160 { "1000baseT", {0, ADVERTISE_1000HALF|ADVERTISE_1000FULL} },
162 #define NMEDIA (sizeof(media)/sizeof(media[0]))
164 /* Parse an argument list of media types */
165 static int parse_media(char *arg, unsigned *bmcr2)
169 mask = strtoul(arg, &s, 16);
170 if ((*arg != '\0') && (*s == '\0')) {
171 if ((mask & LPA_ABILITY_MASK) &&
172 !(mask & ~LPA_ABILITY_MASK)) {
180 s = strtok(arg, ", ");
182 for (i = 0; i < NMEDIA; i++)
183 if (s && strcasecmp(media[i].name, s) == 0) break;
184 if (i == NMEDIA) goto failed;
185 mask |= media[i].value[0];
186 *bmcr2 |= media[i].value[1];
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];
203 if (mask & BMCR_SPEED1000) {
204 if (mask2 & ADVERTISE_1000HALF) {
206 strcat(buf, "1000baseT-HD");
209 if (mask2 & ADVERTISE_1000FULL) {
211 strcat(buf, "1000baseT-FD");
217 for (i = 4; i >= 0; i--) {
220 strcat(buf, media[i].name);
226 strcat(buf, " flow-control");
230 int show_basic_mii(int sock, int phy_id)
234 unsigned bmcr, bmsr, advert, lkpar, bmcr2, lpa2;
236 /* Some bits in the BMSR are latched, but we can't rely on being
237 the only reader, so only the current values are meaningful */
238 mdio_read(sock, MII_BMSR);
239 for (i = 0; i < ((verbose > 1) ? 32 : (MII_STAT1000+1)); i++)
254 case MII_RERRCOUNTER:
262 mii_val[i] = mdio_read(sock, i);
269 if (mii_val[MII_BMCR] == 0xffff || mii_val[MII_BMSR] == 0x0000) {
270 fprintf(stderr, " No MII transceiver present!.\n");
274 /* Descriptive rename. */
275 bmcr = mii_val[MII_BMCR]; bmsr = mii_val[MII_BMSR];
276 advert = mii_val[MII_ADVERTISE]; lkpar = mii_val[MII_LPA];
277 bmcr2 = mii_val[MII_CTRL1000]; lpa2 = mii_val[MII_STAT1000];
279 sprintf(buf, "%s: ", ifr.ifr_name);
280 if (bmcr & BMCR_ANENABLE) {
281 if (bmsr & BMSR_ANEGCOMPLETE) {
282 if (advert & lkpar) {
283 strcat(buf, (lkpar & LPA_LPACK) ?
284 "negotiated" : "no autonegotiation,");
285 strcat(buf, media_list(advert & lkpar, bmcr2 & lpa2>>2, 1));
288 strcat(buf, "autonegotiation failed, ");
290 } else if (bmcr & BMCR_ANRESTART) {
291 strcat(buf, "autonegotiation restarted, ");
294 sprintf(buf+strlen(buf), "%s Mbit, %s duplex, ",
295 ((bmcr2 & (ADVERTISE_1000HALF | ADVERTISE_1000FULL)) & lpa2 >> 2)
297 : (bmcr & BMCR_SPEED100) ? "100" : "10",
298 (bmcr & BMCR_FULLDPLX) ? "full" : "half");
300 strcat(buf, (bmsr & BMSR_LSTATUS) ? "link ok" : "no link");
304 syslog(LOG_INFO, "%s", buf);
307 time_t t = time(NULL);
308 strftime(s, sizeof(s), "%T", localtime(&t));
309 printf("%s %s\n", s, buf);
316 printf(" registers for MII PHY %d: ", phy_id);
317 for (i = 0; i < 32; i++)
318 printf("%s %4.4x", ((i % 8) ? "" : "\n "), mii_val[i]);
323 printf(" product info: ");
324 for (i = 0; i < NMII; i++)
325 if ((mii_id[i].id1 == mii_val[2]) &&
326 (mii_id[i].id2 == (mii_val[3] & 0xfff0)))
329 printf("%s rev %d\n", mii_id[i].name, mii_val[3]&0x0f);
331 printf("vendor %02x:%02x:%02x, model %d rev %d\n",
332 mii_val[2]>>10, (mii_val[2]>>2)&0xff,
333 ((mii_val[2]<<6)|(mii_val[3]>>10))&0xff,
334 (mii_val[3]>>4)&0x3f, mii_val[3]&0x0f);
335 printf(" basic mode: ");
336 if (bmcr & BMCR_RESET)
337 printf("software reset, ");
338 if (bmcr & BMCR_LOOPBACK)
339 printf("loopback, ");
340 if (bmcr & BMCR_ISOLATE)
342 if (bmcr & BMCR_CTST)
343 printf("collision test, ");
344 if (bmcr & BMCR_ANENABLE) {
345 printf("autonegotiation enabled\n");
347 printf("%s Mbit, %s duplex\n",
348 (bmcr & BMCR_SPEED100) ? "100" : "10",
349 (bmcr & BMCR_FULLDPLX) ? "full" : "half");
351 printf(" basic status: ");
352 if (bmsr & BMSR_ANEGCOMPLETE)
353 printf("autonegotiation complete, ");
354 else if (bmcr & BMCR_ANRESTART)
355 printf("autonegotiation restarted, ");
356 if (bmsr & BMSR_RFAULT)
357 printf("remote fault, ");
358 printf((bmsr & BMSR_LSTATUS) ? "link ok" : "no link");
359 printf("\n capabilities:%s", media_list(bmsr >> 6, bmcr2, 0));
360 printf("\n advertising: %s", media_list(advert, bmcr2, 0));
361 if (lkpar & LPA_ABILITY_MASK)
362 printf("\n link partner:%s", media_list(lkpar, lpa2 >> 2, 0));
369 /*--------------------------------------------------------------------*/
371 static int do_one_xcvr(int skfd, char *ifname, int maybe)
373 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
375 /* Get the vitals from the interface. */
376 strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
377 if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
378 if (!maybe || (errno != ENODEV))
379 fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
380 ifname, strerror(errno));
384 if (override_phy >= 0) {
385 printf("using the specified MII index %d.\n", override_phy);
386 mii->phy_id = override_phy;
390 printf("resetting the transceiver...\n");
391 mdio_write(skfd, MII_BMCR, BMCR_RESET);
393 if (nway_advertise > 0) {
394 mdio_write(skfd, MII_ADVERTISE, nway_advertise | 1);
398 printf("restarting autonegotiation...\n");
399 mdio_write(skfd, MII_BMCR, 0x0000);
400 mdio_write(skfd, MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
404 if (fixed_speed & (LPA_100FULL|LPA_100HALF))
405 bmcr |= BMCR_SPEED100;
406 if (fixed_speed & (LPA_100FULL|LPA_10FULL))
407 bmcr |= BMCR_FULLDPLX;
408 mdio_write(skfd, MII_BMCR, bmcr);
411 if (!opt_restart && !opt_reset && !fixed_speed && !nway_advertise)
412 show_basic_mii(skfd, mii->phy_id);
417 /*--------------------------------------------------------------------*/
419 static void watch_one_xcvr(int skfd, char *ifname, int index)
421 struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
422 static int status[MAX_ETH] = { 0, /* ... */ };
425 /* Get the vitals from the interface. */
426 strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
427 if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
429 fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
430 ifname, strerror(errno));
433 now = (mdio_read(skfd, MII_BMCR) |
434 (mdio_read(skfd, MII_BMSR) << 16));
435 if (status[index] && (status[index] != now))
436 show_basic_mii(skfd, mii->phy_id);
440 /*--------------------------------------------------------------------*/
443 "usage: %s [-VvRrwl] [-A media,... | -F media] [interface ...]\n"
444 " -V, --version display version information\n"
445 " -v, --verbose more verbose output\n"
446 " -R, --reset reset MII to poweron state\n"
447 " -r, --restart restart autonegotiation\n"
448 " -w, --watch monitor for link status changes\n"
449 " -l, --log with -w, write events to syslog\n"
450 " -A, --advertise=media,... advertise only specified media\n"
451 " -F, --force=media force specified media technology\n"
452 "media: 1000baseTx-HD, 1000baseTx-FD,\n"
453 " 100baseT4, 100baseTx-FD, 100baseTx-HD,\n"
454 " 10baseT-FD, 10baseT-HD,\n"
455 " (to advertise both HD and FD) 1000baseTx, 100baseTx, 10baseT\n";
458 static void version(void)
460 fprintf(stderr, "%s\n%s\n", Version, RELEASE);
461 exit(5); /* E_VERSION */
465 int main(int argc, char **argv)
467 int i, c, ret, errflag = 0;
469 unsigned ctrl1000 = 0;
471 while ((c = getopt_long(argc, argv, "A:F:p:lrRvVw?", longopts, 0)) != EOF)
473 case 'A': nway_advertise = parse_media(optarg, &ctrl1000); break;
474 case 'F': fixed_speed = parse_media(optarg, &ctrl1000); break;
475 case 'p': override_phy = atoi(optarg); break;
476 case 'r': opt_restart++; break;
477 case 'R': opt_reset++; break;
478 case 'v': verbose++; break;
479 case 'V': opt_version++; break;
480 case 'w': opt_watch++; break;
481 case 'l': opt_log++; break;
484 /* Check for a few inappropriate option combinations */
485 if (opt_watch) verbose = 0;
487 if ((nway_advertise < 0) || (fixed_speed < 0))
490 if (errflag || (fixed_speed & (fixed_speed-1)) ||
491 (fixed_speed && (opt_restart || nway_advertise))) {
492 fprintf(stderr, usage, argv[0]);
499 /* Open a basic socket. */
500 if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
506 printf("Using SIOCGMIIPHY=0x%x\n", SIOCGMIIPHY);
508 /* No remaining args means show all interfaces. */
509 if (optind == argc) {
511 for (i = 0; i < MAX_ETH; i++) {
512 sprintf(s, "eth%d", i);
513 ret &= do_one_xcvr(skfd, s, 1);
516 fprintf(stderr, "no MII interfaces found\n");
519 for (i = optind; i < argc; i++) {
520 ret |= do_one_xcvr(skfd, argv[i], 0);
524 if (opt_watch && (ret == 0)) {
527 if (optind == argc) {
528 for (i = 0; i < MAX_ETH; i++) {
529 sprintf(s, "eth%d", i);
530 watch_one_xcvr(skfd, s, i);
533 for (i = optind; i < argc; i++)
534 watch_one_xcvr(skfd, argv[i], i-optind);