usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / utils / mii-tool.c
blob1a7528e57e3c795763cf85e39168f23d81bcc838
1 /*
3 mii-tool: monitor and control the MII for a network interface
5 Usage:
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
11 harder to use.
13 Copyright (C) 2000 David A. Hinds -- dhinds@pcmcia.sourceforge.org
15 mii-diag is written/copyright 1997-2000 by Donald Becker
16 <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 Donald Becker may be reached as becker@scyld.com, or C/O
23 Scyld Computing Corporation, 410 Severn Av., Suite 210,
24 Annapolis, MD 21403
26 References
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";
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <getopt.h>
43 #include <time.h>
44 #include <syslog.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/ioctl.h>
48 #include <net/if.h>
49 #include <linux/sockios.h>
51 #ifndef __GLIBC__
52 #include <linux/if_arp.h>
53 #include <linux/if_ether.h>
54 #endif
55 #include "mii.h"
57 #define MAX_ETH 8 /* Maximum # of interfaces */
59 /* Table of known MII's */
60 static const struct {
61 u_short id1, id2;
62 const char *name;
63 } mii_id[] = {
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 */
99 { 0, 0, 0, 0 }
102 static unsigned int
103 verbose = 0,
104 opt_version = 0,
105 opt_restart = 0,
106 opt_reset = 0,
107 opt_log = 0,
108 opt_watch = 0;
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,
125 strerror(errno));
126 return -1;
128 return mii->val_out;
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;
136 mii->val_in = value;
137 if (ioctl(skfd, SIOCSMIIREG, &ifr) < 0) {
138 fprintf(stderr, "SIOCSMIIREG on %s failed: %s\n", ifr.ifr_name,
139 strerror(errno));
143 /*--------------------------------------------------------------------*/
145 const struct {
146 char *name;
147 u_short value[2];
148 } media[] = {
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)
167 int i;
168 u_long mask;
169 char *s;
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)) {
175 return mask;
177 goto failed;
179 mask = 0;
180 s = strtok(arg, ", ");
181 do {
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);
189 return mask;
190 failed:
191 fprintf(stderr, "Invalid media specification '%s'.\n", arg);
192 return -1;
195 /*--------------------------------------------------------------------*/
197 static const char *media_list(unsigned mask, unsigned mask2, int best)
199 static char buf[100];
200 int i;
201 *buf = '\0';
203 if (supports_gmii) {
204 for (i = 8; i >= 7; i--) {
205 if (mask2 & media[i].value[1]) {
206 strcat(buf, " ");
207 strcat(buf, media[i].name);
208 if (best) goto out;
213 mask >>= 5;
214 for (i = 4; i >= 0; i--) {
215 if (mask & (1<<i)) {
216 strcat(buf, " ");
217 strcat(buf, media[i].name);
218 if (best) break;
221 out:
222 if (mask & (1<<5))
223 strcat(buf, " flow-control");
224 return buf;
227 static int show_basic_mii(int sock, int phy_id)
229 char buf[128];
230 int i;
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");
242 return -1;
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));
259 strcat(buf, ", ");
260 } else {
261 strcat(buf, "autonegotiation failed, ");
263 } else if (bmcr & MII_BMCR_RESTART) {
264 strcat(buf, "autonegotiation restarted, ");
266 } else {
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");
274 if (opt_watch) {
275 if (opt_log) {
276 syslog(LOG_INFO, buf);
277 } else {
278 char s[20];
279 time_t t = time(NULL);
280 strftime(s, sizeof(s), "%T", localtime(&t));
281 printf("%s %s\n", s, buf);
283 } else {
284 printf("%s\n", buf);
287 if (verbose > 1) {
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]);
291 printf("\n");
294 if (verbose) {
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)))
299 break;
300 if (i < NMII)
301 printf("%s rev %d\n", mii_id[i].name, mii_val[MII_PHY_ID2]&0x0f);
302 else
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)
313 printf("isolate, ");
314 if (bmcr & MII_BMCR_COLTEST)
315 printf("collision test, ");
316 if (bmcr & MII_BMCR_AN_ENA) {
317 printf("autonegotiation enabled\n");
318 } else {
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));
336 printf("\n");
338 return 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));
353 return 1;
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) {
360 mii->phy_id = 0;
363 supports_gmii = 0;
364 if (mdio_read(skfd, MII_BMSR) & MII_BMSR_ESTATEN)
365 supports_gmii = mdio_read(skfd, MII_ESTAT1000) & 0xf000;
367 return 0;
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))
377 return 1;
379 if (opt_reset) {
380 printf("resetting the transceiver...\n");
381 mdio_write(skfd, MII_BMCR, MII_BMCR_RESET);
383 if (nway_advertise) {
384 if (supports_gmii) {
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);
391 opt_restart = 1;
393 if (opt_restart) {
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);
398 if (fixed_speed) {
399 int bmcr = 0;
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);
412 return 0;
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, /* ... */ };
421 u_long now;
423 if (ifr_prep(skfd, ifname, 0))
424 return;
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);
430 status[index] = now;
433 /*--------------------------------------------------------------------*/
435 const char *usage =
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;
454 char s[6];
456 while ((c = getopt_long(argc, argv, "A:F:p:lrRvVw?", longopts, 0)) != EOF)
457 switch (c) {
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]);
474 return 2;
477 if (opt_version)
478 printf(version);
480 /* Open a basic socket. */
481 if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
482 perror("socket");
483 exit(-1);
486 /* No remaining args means show all interfaces. */
487 if (optind == argc) {
488 ret = 1;
489 for (i = 0; i < MAX_ETH; i++) {
490 sprintf(s, "eth%d", i);
491 ret &= do_one_xcvr(skfd, s, 1);
493 if (ret)
494 fprintf(stderr, "no MII interfaces found\n");
495 } else {
496 ret = 0;
497 for (i = optind; i < argc; i++) {
498 ret |= do_one_xcvr(skfd, argv[i], 0);
502 if (opt_watch && (ret == 0)) {
503 while (1) {
504 sleep(1);
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);
510 } else {
511 for (i = optind; i < argc; i++)
512 watch_one_xcvr(skfd, argv[i], i-optind);
517 close(skfd);
518 return ret;