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 2000/04/28 00:56:08 (David Hinds)\n";
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/ioctl.h>
50 #include <linux/if_arp.h>
51 #include <linux/if_ether.h>
55 #define MAX_ETH 8 /* Maximum # of interfaces */
57 /* Table of known MII's */
62 { 0x0022, 0x5610, "AdHoc AH101LF" },
63 { 0x0022, 0x5520, "Altimata AC101LF" },
64 { 0x0000, 0x6b90, "AMD 79C901A HomePNA" },
65 { 0x0000, 0x6b70, "AMD 79C901A 10baseT" },
66 { 0x0181, 0xb800, "Davicom DM9101" },
67 { 0x0043, 0x7411, "Enable EL40-331" },
68 { 0x0015, 0xf410, "ICS 1889" },
69 { 0x0015, 0xf420, "ICS 1890" },
70 { 0x0015, 0xf430, "ICS 1892" },
71 { 0x02a8, 0x0150, "Intel 82555" },
72 { 0x7810, 0x0000, "Level One LXT970/971" },
73 { 0x2000, 0x5c00, "National DP83840A" },
74 { 0x0181, 0x4410, "Quality QS6612" },
75 { 0x0282, 0x1c50, "SMSC 83C180" },
76 { 0x0300, 0xe540, "TDK 78Q2120" },
78 #define NMII (sizeof(mii_id)/sizeof(mii_id[0]))
80 /*--------------------------------------------------------------------*/
82 struct option longopts
[] = {
83 /* { name has_arg *flag val } */
84 {"advertise", 1, 0, 'A'}, /* Change capabilities advertised. */
85 {"force", 1, 0, 'F'}, /* Change capabilities advertised. */
86 {"phy", 1, 0, 'p'}, /* Set PHY (MII address) to report. */
87 {"log", 0, 0, 'l'}, /* Set PHY (MII address) to report. */
88 {"restart", 0, 0, 'r'}, /* Restart link negotiation */
89 {"reset", 0, 0, 'R'}, /* Reset the transceiver. */
90 {"verbose", 0, 0, 'v'}, /* Report each action taken. */
91 {"version", 0, 0, 'V'}, /* Emit version information. */
92 {"watch", 0, 0, 'w'}, /* Constantly monitor the port. */
93 {"help", 0, 0, '?'}, /* Give help */
104 static int nway_advertise
= 0;
105 static int fixed_speed
= 0;
106 static int override_phy
= -1;
108 static int skfd
= -1; /* AF_INET socket for ioctl() calls. */
109 static struct ifreq ifr
;
111 /*--------------------------------------------------------------------*/
113 static int mdio_read(int skfd
, int location
)
115 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
116 mii
->reg_num
= location
;
117 if (ioctl(skfd
, SIOCGMIIREG
, &ifr
) < 0) {
118 fprintf(stderr
, "SIOCGMIIREG on %s failed: %s\n", ifr
.ifr_name
,
125 static void mdio_write(int skfd
, int location
, int value
)
127 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
128 mii
->reg_num
= location
;
130 if (ioctl(skfd
, SIOCSMIIREG
, &ifr
) < 0) {
131 fprintf(stderr
, "SIOCSMIIREG on %s failed: %s\n", ifr
.ifr_name
,
136 /*--------------------------------------------------------------------*/
142 /* The order through 100baseT4 matches bits in the BMSR */
143 { "10baseT-HD", MII_AN_10BASET_HD
},
144 { "10baseT-FD", MII_AN_10BASET_FD
},
145 { "100baseTx-HD", MII_AN_100BASETX_HD
},
146 { "100baseTx-FD", MII_AN_100BASETX_FD
},
147 { "100baseT4", MII_AN_100BASET4
},
148 { "100baseTx", MII_AN_100BASETX_FD
| MII_AN_100BASETX_HD
},
149 { "10baseT", MII_AN_10BASET_FD
| MII_AN_10BASET_HD
},
151 #define NMEDIA (sizeof(media)/sizeof(media[0]))
153 /* Parse an argument list of media types */
154 static int parse_media(char *arg
)
158 mask
= strtoul(arg
, &s
, 16);
159 if ((*arg
!= '\0') && (*s
== '\0')) {
160 if ((mask
& MII_AN_ABILITY_MASK
) &&
161 !(mask
& ~MII_AN_ABILITY_MASK
))
166 s
= strtok(arg
, ", ");
168 for (i
= 0; i
< NMEDIA
; i
++)
169 if (s
&& strcasecmp(media
[i
].name
, s
) == 0) break;
170 if (i
== NMEDIA
) goto failed
;
171 mask
|= media
[i
].value
;
172 } while ((s
= strtok(NULL
, ", ")) != NULL
);
176 fprintf(stderr
, "Invalid media specification '%s'.\n", arg
);
180 /*--------------------------------------------------------------------*/
182 static char *media_list(int mask
, int best
)
184 static char buf
[100];
188 for (i
= 4; i
>= 0; i
--) {
191 strcat(buf
, media
[i
].name
);
196 strcat(buf
, " flow-control");
200 int show_basic_mii(int sock
, int phy_id
)
204 int bmcr
, bmsr
, advert
, lkpar
;
206 /* Some bits in the BMSR are latched, but we can't rely on being
207 the only reader, so only the current values are meaningful */
208 mdio_read(sock
, MII_BMSR
);
209 for (i
= 0; i
< ((verbose
> 1) ? 32 : 8); i
++)
210 mii_val
[i
] = mdio_read(sock
, i
);
212 if (mii_val
[MII_BMCR
] == 0xffff) {
213 fprintf(stderr
, " No MII transceiver present!.\n");
217 /* Descriptive rename. */
218 bmcr
= mii_val
[MII_BMCR
]; bmsr
= mii_val
[MII_BMSR
];
219 advert
= mii_val
[MII_ANAR
]; lkpar
= mii_val
[MII_ANLPAR
];
221 sprintf(buf
, "%s: ", ifr
.ifr_name
);
222 if (bmcr
& MII_BMCR_AN_ENA
) {
223 if (bmsr
& MII_BMSR_AN_COMPLETE
) {
224 if (advert
& lkpar
) {
225 strcat(buf
, (lkpar
& MII_AN_ACK
) ?
226 "negotiated" : "no autonegotiation,");
227 strcat(buf
, media_list(advert
& lkpar
, 1));
230 strcat(buf
, "autonegotiation failed, ");
232 } else if (bmcr
& MII_BMCR_RESTART
) {
233 strcat(buf
, "autonegotiation restarted, ");
236 sprintf(buf
+strlen(buf
), "%s Mbit, %s duplex, ",
237 (bmcr
& MII_BMCR_100MBIT
) ? "100" : "10",
238 (bmcr
& MII_BMCR_DUPLEX
) ? "full" : "half");
240 strcat(buf
, (bmsr
& MII_BMSR_LINK_VALID
) ? "link ok" : "no link");
244 syslog(LOG_INFO
, buf
);
247 time_t t
= time(NULL
);
248 strftime(s
, sizeof(s
), "%T", localtime(&t
));
249 printf("%s %s\n", s
, buf
);
256 printf(" registers for MII PHY %d: ", phy_id
);
257 for (i
= 0; i
< 32; i
++)
258 printf("%s %4.4x", ((i
% 8) ? "" : "\n "), mii_val
[i
]);
263 printf(" product info: ");
264 for (i
= 0; i
< NMII
; i
++)
265 if ((mii_id
[i
].id1
== mii_val
[2]) &&
266 (mii_id
[i
].id2
== (mii_val
[3] & 0xfff0)))
269 printf("%s rev %d\n", mii_id
[i
].name
, mii_val
[3]&0x0f);
271 printf("vendor %02x:%02x:%02x, model %d rev %d\n",
272 mii_val
[2]>>10, (mii_val
[2]>>2)&0xff,
273 ((mii_val
[2]<<6)|(mii_val
[3]>>10))&0xff,
274 (mii_val
[3]>>4)&0x3f, mii_val
[3]&0x0f);
275 printf(" basic mode: ");
276 if (bmcr
& MII_BMCR_RESET
)
277 printf("software reset, ");
278 if (bmcr
& MII_BMCR_LOOPBACK
)
279 printf("loopback, ");
280 if (bmcr
& MII_BMCR_ISOLATE
)
282 if (bmcr
& MII_BMCR_COLTEST
)
283 printf("collision test, ");
284 if (bmcr
& MII_BMCR_AN_ENA
) {
285 printf("autonegotiation enabled\n");
287 printf("%s Mbit, %s duplex\n",
288 (bmcr
& MII_BMCR_100MBIT
) ? "100" : "10",
289 (bmcr
& MII_BMCR_DUPLEX
) ? "full" : "half");
291 printf(" basic status: ");
292 if (bmsr
& MII_BMSR_AN_COMPLETE
)
293 printf("autonegotiation complete, ");
294 else if (bmcr
& MII_BMCR_RESTART
)
295 printf("autonegotiation restarted, ");
296 if (bmsr
& MII_BMSR_REMOTE_FAULT
)
297 printf("remote fault, ");
298 printf((bmsr
& MII_BMSR_LINK_VALID
) ? "link ok" : "no link");
299 printf("\n capabilities:%s", media_list(bmsr
>> 6, 0));
300 printf("\n advertising: %s", media_list(advert
, 0));
301 if (lkpar
& MII_AN_ABILITY_MASK
)
302 printf("\n link partner:%s", media_list(lkpar
, 0));
309 /*--------------------------------------------------------------------*/
311 static int do_one_xcvr(int skfd
, char *ifname
, int maybe
)
313 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
315 /* Get the vitals from the interface. */
316 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
317 if (ioctl(skfd
, SIOCGMIIPHY
, &ifr
) < 0) {
318 if (!maybe
|| (errno
!= ENODEV
))
319 fprintf(stderr
, "SIOCGMIIPHY on '%s' failed: %s\n",
320 ifname
, strerror(errno
));
324 if (override_phy
>= 0) {
325 printf("using the specified MII index %d.\n", override_phy
);
326 mii
->phy_id
= override_phy
;
330 printf("resetting the transceiver...\n");
331 mdio_write(skfd
, MII_BMCR
, MII_BMCR_RESET
);
333 if (nway_advertise
> 0) {
334 mdio_write(skfd
, MII_ANAR
, nway_advertise
| 1);
338 printf("restarting autonegotiation...\n");
339 mdio_write(skfd
, MII_BMCR
, 0x0000);
340 mdio_write(skfd
, MII_BMCR
, MII_BMCR_AN_ENA
|MII_BMCR_RESTART
);
344 if (fixed_speed
& (MII_AN_100BASETX_FD
|MII_AN_100BASETX_HD
))
345 bmcr
|= MII_BMCR_100MBIT
;
346 if (fixed_speed
& (MII_AN_100BASETX_FD
|MII_AN_10BASET_FD
))
347 bmcr
|= MII_BMCR_DUPLEX
;
348 mdio_write(skfd
, MII_BMCR
, bmcr
);
351 if (!opt_restart
&& !opt_reset
&& !fixed_speed
&& !nway_advertise
)
352 show_basic_mii(skfd
, mii
->phy_id
);
357 /*--------------------------------------------------------------------*/
359 static void watch_one_xcvr(int skfd
, char *ifname
, int index
)
361 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
362 static int status
[MAX_ETH
] = { 0, /* ... */ };
365 /* Get the vitals from the interface. */
366 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
367 if (ioctl(skfd
, SIOCGMIIPHY
, &ifr
) < 0) {
369 fprintf(stderr
, "SIOCGMIIPHY on '%s' failed: %s\n",
370 ifname
, strerror(errno
));
373 now
= (mdio_read(skfd
, MII_BMCR
) |
374 (mdio_read(skfd
, MII_BMSR
) << 16));
375 if (status
[index
] && (status
[index
] != now
))
376 show_basic_mii(skfd
, mii
->phy_id
);
380 /*--------------------------------------------------------------------*/
383 "usage: %s [-VvRrwl] [-A media,... | -F media] [interface ...]
384 -V, --version display version information
385 -v, --verbose more verbose output
386 -R, --reset reset MII to poweron state
387 -r, --restart restart autonegotiation
388 -w, --watch monitor for link status changes
389 -l, --log with -w, write events to syslog
390 -A, --advertise=media,... advertise only specified media
391 -F, --force=media force specified media technology
392 media: 100baseT4, 100baseTx-FD, 100baseTx-HD, 10baseT-FD, 10baseT-HD,
393 (to advertise both HD and FD) 100baseTx, 10baseT\n";
395 int main(int argc
, char **argv
)
397 int i
, c
, ret
, errflag
= 0;
400 while ((c
= getopt_long(argc
, argv
, "A:F:p:lrRvVw?", longopts
, 0)) != EOF
)
402 case 'A': nway_advertise
= parse_media(optarg
); break;
403 case 'F': fixed_speed
= parse_media(optarg
); break;
404 case 'p': override_phy
= atoi(optarg
); break;
405 case 'r': opt_restart
++; break;
406 case 'R': opt_reset
++; break;
407 case 'v': verbose
++; break;
408 case 'V': opt_version
++; break;
409 case 'w': opt_watch
++; break;
410 case 'l': opt_log
++; break;
413 /* Check for a few inappropriate option combinations */
414 if (opt_watch
) verbose
= 0;
416 if ((nway_advertise
< 0) || (fixed_speed
< 0))
419 if (errflag
|| (fixed_speed
& (fixed_speed
-1)) ||
420 (fixed_speed
&& (opt_restart
|| nway_advertise
))) {
421 fprintf(stderr
, usage
, argv
[0]);
428 /* Open a basic socket. */
429 if ((skfd
= socket(AF_INET
, SOCK_DGRAM
,0)) < 0) {
434 /* No remaining args means show all interfaces. */
435 if (optind
== argc
) {
437 for (i
= 0; i
< MAX_ETH
; i
++) {
438 sprintf(s
, "eth%d", i
);
439 ret
&= do_one_xcvr(skfd
, s
, 1);
442 fprintf(stderr
, "no MII interfaces found\n");
445 for (i
= optind
; i
< argc
; i
++) {
446 ret
|= do_one_xcvr(skfd
, argv
[i
], 0);
450 if (opt_watch
&& (ret
== 0)) {
453 if (optind
== argc
) {
454 for (i
= 0; i
< MAX_ETH
; i
++) {
455 sprintf(s
, "eth%d", i
);
456 watch_one_xcvr(skfd
, s
, i
);
459 for (i
= optind
; i
< argc
; i
++)
460 watch_one_xcvr(skfd
, argv
[i
], i
-optind
);