minor fix to return E_USAGE on -V instead of exit(0);
[oss-qm-packages.git] / mii-tool.c
blobef2d48a434a229537f34f6d89f6a4d987bd1bc06
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 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 #ifndef __GLIBC__
50 #include <linux/if_arp.h>
51 #include <linux/if_ether.h>
52 #endif
53 #include "mii.h"
55 #define MAX_ETH 8 /* Maximum # of interfaces */
57 /* Table of known MII's */
58 static struct {
59 u_short id1, id2;
60 char *name;
61 } mii_id[] = {
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 */
94 { 0, 0, 0, 0 }
97 static unsigned int
98 verbose = 0,
99 opt_version = 0,
100 opt_restart = 0,
101 opt_reset = 0,
102 opt_log = 0,
103 opt_watch = 0;
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,
119 strerror(errno));
120 return -1;
122 return mii->val_out;
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;
129 mii->val_in = value;
130 if (ioctl(skfd, SIOCSMIIREG, &ifr) < 0) {
131 fprintf(stderr, "SIOCSMIIREG on %s failed: %s\n", ifr.ifr_name,
132 strerror(errno));
136 /*--------------------------------------------------------------------*/
138 const struct {
139 char *name;
140 u_short value;
141 } media[] = {
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)
156 int mask, i;
157 char *s;
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))
162 return mask;
163 goto failed;
164 } else {
165 mask = 0;
166 s = strtok(arg, ", ");
167 do {
168 for (i = 0; i < NMEDIA; i++)
169 if (strcasecmp(media[i].name, s) == 0) break;
170 if (i == NMEDIA) goto failed;
171 mask |= media[i].value;
172 } while ((s = strtok(NULL, ", ")) != NULL);
174 return mask;
175 failed:
176 fprintf(stderr, "Invalid media specification '%s'.\n", arg);
177 return -1;
180 /*--------------------------------------------------------------------*/
182 static char *media_list(int mask, int best)
184 static char buf[100];
185 int i;
186 *buf = '\0';
187 mask >>= 5;
188 for (i = 4; i >= 0; i--) {
189 if (mask & (1<<i)) {
190 strcat(buf, " ");
191 strcat(buf, media[i].name);
192 if (best) break;
195 if (mask & (1<<5))
196 strcat(buf, " flow-control");
197 return buf;
200 int show_basic_mii(int sock, int phy_id)
202 char buf[100];
203 int i, mii_val[32];
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");
214 return -1;
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));
228 strcat(buf, ", ");
229 } else {
230 strcat(buf, "autonegotiation failed, ");
232 } else if (bmcr & MII_BMCR_RESTART) {
233 strcat(buf, "autonegotiation restarted, ");
235 } else {
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");
242 if (opt_watch) {
243 if (opt_log) {
244 syslog(LOG_INFO, buf);
245 } else {
246 char s[20];
247 time_t t = time(NULL);
248 strftime(s, sizeof(s), "%T", localtime(&t));
249 printf("%s %s\n", s, buf);
251 } else {
252 printf("%s\n", buf);
255 if (verbose > 1) {
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]);
259 printf("\n");
262 if (verbose) {
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)))
267 break;
268 if (i < NMII)
269 printf("%s rev %d\n", mii_id[i].name, mii_val[3]&0x0f);
270 else
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)
281 printf("isolate, ");
282 if (bmcr & MII_BMCR_COLTEST)
283 printf("collision test, ");
284 if (bmcr & MII_BMCR_AN_ENA) {
285 printf("autonegotiation enabled\n");
286 } else {
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));
303 printf("\n");
305 return 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));
320 return 1;
323 if (override_phy >= 0) {
324 printf("using the specified MII index %d.\n", override_phy);
325 mii->phy_id = override_phy;
328 if (opt_reset) {
329 printf("resetting the transceiver...\n");
330 mdio_write(skfd, MII_BMCR, MII_BMCR_RESET);
332 if (nway_advertise) {
333 mdio_write(skfd, MII_ANAR, nway_advertise | 1);
334 opt_restart = 1;
336 if (opt_restart) {
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);
341 if (fixed_speed) {
342 int bmcr = 0;
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);
353 return 0;
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, /* ... */ };
362 int now;
364 /* Get the vitals from the interface. */
365 strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
366 if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
367 if (errno != ENODEV)
368 fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
369 ifname, strerror(errno));
370 return;
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);
376 status[index] = now;
379 /*--------------------------------------------------------------------*/
381 const char *usage =
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;
397 char s[6];
399 while ((c = getopt_long(argc, argv, "A:F:p:lrRvVw?", longopts, 0)) != EOF)
400 switch (c) {
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;
410 case '?': errflag++;
412 /* Check for a few inappropriate option combinations */
413 if (opt_watch) verbose = 0;
414 if (errflag || (fixed_speed & (fixed_speed-1)) ||
415 (fixed_speed && (opt_restart || nway_advertise))) {
416 fprintf(stderr, usage, argv[0]);
417 return 2;
420 if (opt_version)
421 printf(version);
423 /* Open a basic socket. */
424 if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
425 perror("socket");
426 exit(-1);
429 /* No remaining args means show all interfaces. */
430 if (optind == argc) {
431 ret = 1;
432 for (i = 0; i < MAX_ETH; i++) {
433 sprintf(s, "eth%d", i);
434 ret &= do_one_xcvr(skfd, s, 1);
436 if (ret)
437 fprintf(stderr, "no MII interfaces found\n");
438 } else {
439 ret = 0;
440 for (i = optind; i < argc; i++) {
441 ret |= do_one_xcvr(skfd, argv[i], 0);
445 if (opt_watch && (ret == 0)) {
446 while (1) {
447 sleep(1);
448 if (optind == argc) {
449 for (i = 0; i < MAX_ETH; i++) {
450 sprintf(s, "eth%d", i);
451 watch_one_xcvr(skfd, s, i);
453 } else {
454 for (i = optind; i < argc; i++)
455 watch_one_xcvr(skfd, argv[i], i-optind);
460 close(skfd);
461 return ret;