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));
308 /*--------------------------------------------------------------------*/
310 static int do_one_xcvr(int skfd
, char *ifname
, int maybe
)
312 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
314 /* Get the vitals from the interface. */
315 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
316 if (ioctl(skfd
, SIOCGMIIPHY
, &ifr
) < 0) {
317 if (!maybe
|| (errno
!= ENODEV
))
318 fprintf(stderr
, "SIOCGMIIPHY on '%s' failed: %s\n",
319 ifname
, strerror(errno
));
323 if (override_phy
>= 0) {
324 printf("using the specified MII index %d.\n", override_phy
);
325 mii
->phy_id
= override_phy
;
329 printf("resetting the transceiver...\n");
330 mdio_write(skfd
, MII_BMCR
, MII_BMCR_RESET
);
332 if (nway_advertise
> 0) {
333 mdio_write(skfd
, MII_ANAR
, nway_advertise
| 1);
337 printf("restarting autonegotiation...\n");
338 mdio_write(skfd
, MII_BMCR
, 0x0000);
339 mdio_write(skfd
, MII_BMCR
, MII_BMCR_AN_ENA
|MII_BMCR_RESTART
);
343 if (fixed_speed
& (MII_AN_100BASETX_FD
|MII_AN_100BASETX_HD
))
344 bmcr
|= MII_BMCR_100MBIT
;
345 if (fixed_speed
& (MII_AN_100BASETX_FD
|MII_AN_10BASET_FD
))
346 bmcr
|= MII_BMCR_DUPLEX
;
347 mdio_write(skfd
, MII_BMCR
, bmcr
);
350 if (!opt_restart
&& !opt_reset
&& !fixed_speed
&& !nway_advertise
)
351 show_basic_mii(skfd
, mii
->phy_id
);
356 /*--------------------------------------------------------------------*/
358 static void watch_one_xcvr(int skfd
, char *ifname
, int index
)
360 struct mii_data
*mii
= (struct mii_data
*)&ifr
.ifr_data
;
361 static int status
[MAX_ETH
] = { 0, /* ... */ };
364 /* Get the vitals from the interface. */
365 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
366 if (ioctl(skfd
, SIOCGMIIPHY
, &ifr
) < 0) {
368 fprintf(stderr
, "SIOCGMIIPHY on '%s' failed: %s\n",
369 ifname
, strerror(errno
));
372 now
= (mdio_read(skfd
, MII_BMCR
) |
373 (mdio_read(skfd
, MII_BMSR
) << 16));
374 if (status
[index
] && (status
[index
] != now
))
375 show_basic_mii(skfd
, mii
->phy_id
);
379 /*--------------------------------------------------------------------*/
382 "usage: %s [-VvRrwl] [-A media,... | -F media] [interface ...]
383 -V, --version display version information
384 -v, --verbose more verbose output
385 -R, --reset reset MII to poweron state
386 -r, --restart restart autonegotiation
387 -w, --watch monitor for link status changes
388 -l, --log with -w, write events to syslog
389 -A, --advertise=media,... advertise only specified media
390 -F, --force=media force specified media technology
391 media: 100baseT4, 100baseTx-FD, 100baseTx-HD, 10baseT-FD, 10baseT-HD,
392 (to advertise both HD and FD) 100baseTx, 10baseT\n";
394 int main(int argc
, char **argv
)
396 int i
, c
, ret
, errflag
= 0;
399 while ((c
= getopt_long(argc
, argv
, "A:F:p:lrRvVw?", longopts
, 0)) != EOF
)
401 case 'A': nway_advertise
= parse_media(optarg
); break;
402 case 'F': fixed_speed
= parse_media(optarg
); break;
403 case 'p': override_phy
= atoi(optarg
); break;
404 case 'r': opt_restart
++; break;
405 case 'R': opt_reset
++; break;
406 case 'v': verbose
++; break;
407 case 'V': opt_version
++; break;
408 case 'w': opt_watch
++; break;
409 case 'l': opt_log
++; break;
412 /* Check for a few inappropriate option combinations */
413 if (opt_watch
) verbose
= 0;
415 if ((nway_advertise
< 0) || (fixed_speed
< 0))
418 if (errflag
|| (fixed_speed
& (fixed_speed
-1)) ||
419 (fixed_speed
&& (opt_restart
|| nway_advertise
))) {
420 fprintf(stderr
, usage
, argv
[0]);
427 /* Open a basic socket. */
428 if ((skfd
= socket(AF_INET
, SOCK_DGRAM
,0)) < 0) {
433 /* No remaining args means show all interfaces. */
434 if (optind
== argc
) {
436 for (i
= 0; i
< MAX_ETH
; i
++) {
437 sprintf(s
, "eth%d", i
);
438 ret
&= do_one_xcvr(skfd
, s
, 1);
441 fprintf(stderr
, "no MII interfaces found\n");
444 for (i
= optind
; i
< argc
; i
++) {
445 ret
|= do_one_xcvr(skfd
, argv
[i
], 0);
449 if (opt_watch
&& (ret
== 0)) {
452 if (optind
== argc
) {
453 for (i
= 0; i
< MAX_ETH
; i
++) {
454 sprintf(s
, "eth%d", i
);
455 watch_one_xcvr(skfd
, s
, i
);
458 for (i
= optind
; i
< argc
; i
++)
459 watch_one_xcvr(skfd
, argv
[i
], i
-optind
);