OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / net-tools / mii-tool.c
1 /*
2
3     mii-tool: monitor and control the MII for a network interface
4
5     Usage:
6
7         mii-tool [-VvRrw] [-A media,... | -F media] [interface ...]
8
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.
12
13     Copyright (C) 2000 David A. Hinds -- dhinds@pcmcia.sourceforge.org
14
15     mii-diag is written/copyright 1997-2000 by Donald Becker
16         <becker@scyld.com>
17
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.
21
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
25
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
30 */
31
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)";
33
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <getopt.h>
42 #include <time.h>
43 #include <syslog.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/ioctl.h>
47 #include <net/if.h>
48 #include <linux/sockios.h>
49
50 #ifndef __GLIBC__
51 #include <linux/if_arp.h>
52 #include <linux/if_ether.h>
53 #endif
54 #include "mii.h"
55 #include "version.h"
56
57 #define MAX_ETH         8               /* Maximum # of interfaces */
58
59 /* Table of known MII's */
60 static const struct {
61     u_short     id1, id2;
62     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" },
82 };
83 #define NMII (sizeof(mii_id)/sizeof(mii_id[0]))
84
85 /*--------------------------------------------------------------------*/
86
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'},     /* Set PHY (MII address) to report. */
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 }
100 };
101
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 int nway_advertise = 0;
110 static int fixed_speed = 0;
111 static int override_phy = -1;
112
113 static int skfd = -1;           /* AF_INET socket for ioctl() calls. */
114 static struct ifreq ifr;
115
116 /*--------------------------------------------------------------------*/
117
118 static int mdio_read(int skfd, int location)
119 {
120     struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
121     mii->reg_num = location;
122     if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) {
123         fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,
124                 strerror(errno));
125         return -1;
126     }
127     return mii->val_out;
128 }
129
130 static void mdio_write(int skfd, int location, int value)
131 {
132     struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
133     mii->reg_num = location;
134     mii->val_in = value;
135     if (ioctl(skfd, SIOCSMIIREG, &ifr) < 0) {
136         fprintf(stderr, "SIOCSMIIREG on %s failed: %s\n", ifr.ifr_name,
137                 strerror(errno));
138     }
139 }
140
141 /*--------------------------------------------------------------------*/
142
143 const struct {
144     char        *name;
145     u_short     value[2];
146 } media[] = {
147     /* The order through 100baseT4 matches bits in the BMSR */
148     { "10baseT-HD",     {MII_AN_10BASET_HD} },
149     { "10baseT-FD",     {MII_AN_10BASET_FD} },
150     { "100baseTx-HD",   {MII_AN_100BASETX_HD} },
151     { "100baseTx-FD",   {MII_AN_100BASETX_FD} },
152     { "100baseT4",      {MII_AN_100BASET4} },
153     { "100baseTx",      {MII_AN_100BASETX_FD | MII_AN_100BASETX_HD} },
154     { "10baseT",        {MII_AN_10BASET_FD | MII_AN_10BASET_HD} },
155
156     { "1000baseT-HD",   {0, MII_BMCR2_1000HALF} },
157     { "1000baseT-FD",   {0, MII_BMCR2_1000FULL} },
158     { "1000baseT",      {0, MII_BMCR2_1000HALF|MII_BMCR2_1000FULL} },
159 };
160 #define NMEDIA (sizeof(media)/sizeof(media[0]))
161         
162 /* Parse an argument list of media types */
163 static int parse_media(char *arg, unsigned *bmcr2)
164 {
165     int mask, i;
166     char *s;
167     mask = strtoul(arg, &s, 16);
168     if ((*arg != '\0') && (*s == '\0')) {
169         if ((mask & MII_AN_ABILITY_MASK) &&
170             !(mask & ~MII_AN_ABILITY_MASK)) {
171                 *bmcr2 = 0;
172                 return mask;
173         }
174         goto failed;
175     }
176     mask = 0;
177     *bmcr2 = 0;
178     s = strtok(arg, ", ");
179     do {
180             for (i = 0; i < NMEDIA; i++)
181                 if (s && strcasecmp(media[i].name, s) == 0) break;
182             if (i == NMEDIA) goto failed;
183             mask |= media[i].value[0];
184             *bmcr2 |= media[i].value[1];
185     } while ((s = strtok(NULL, ", ")) != NULL);
186
187     return mask;
188 failed:
189     fprintf(stderr, "Invalid media specification '%s'.\n", arg);
190     return -1;
191 }
192
193 /*--------------------------------------------------------------------*/
194
195 static const char *media_list(unsigned mask, unsigned mask2, int best)
196 {
197     static char buf[100];
198     int i;
199     *buf = '\0';
200
201     if (mask & MII_BMCR_SPEED1000) {
202         if (mask2 & MII_BMCR2_1000HALF) {
203             strcat(buf, " ");
204             strcat(buf, "1000baseT-HD");
205             if (best) goto out;
206         }
207         if (mask2 & MII_BMCR2_1000FULL) {
208             strcat(buf, " ");
209             strcat(buf, "1000baseT-FD");
210             if (best) goto out;
211         }
212     }
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;
219         }
220     }
221  out:
222     if (mask & (1<<5))
223         strcat(buf, " flow-control");
224     return buf;
225 }
226
227 int show_basic_mii(int sock, int phy_id)
228 {
229     char buf[100];
230     int i, mii_val[32];
231     unsigned bmcr, bmsr, advert, lkpar, bmcr2, lpa2;
232
233     /* Some bits in the BMSR are latched, but we can't rely on being
234        the only reader, so only the current values are meaningful */
235     mdio_read(sock, MII_BMSR);
236     for (i = 0; i < ((verbose > 1) ? 32 : MII_BASIC_MAX); i++)
237         mii_val[i] = mdio_read(sock, i);
238
239     if (mii_val[MII_BMCR] == 0xffff  || mii_val[MII_BMSR] == 0x0000) {
240         fprintf(stderr, "  No MII transceiver present!.\n");
241         return -1;
242     }
243
244     /* Descriptive rename. */
245     bmcr = mii_val[MII_BMCR]; bmsr = mii_val[MII_BMSR];
246     advert = mii_val[MII_ANAR]; lkpar = mii_val[MII_ANLPAR];
247     bmcr2 = mii_val[MII_CTRL1000]; lpa2 = mii_val[MII_STAT1000];
248
249     sprintf(buf, "%s: ", ifr.ifr_name);
250     if (bmcr & MII_BMCR_AN_ENA) {
251         if (bmsr & MII_BMSR_AN_COMPLETE) {
252             if (advert & lkpar) {
253                 strcat(buf, (lkpar & MII_AN_ACK) ?
254                        "negotiated" : "no autonegotiation,");
255                 strcat(buf, media_list(advert & lkpar, bmcr2 & lpa2>>2, 1));
256                 strcat(buf, ", ");
257             } else {
258                 strcat(buf, "autonegotiation failed, ");
259             }
260         } else if (bmcr & MII_BMCR_RESTART) {
261             strcat(buf, "autonegotiation restarted, ");
262         }
263     } else {
264         sprintf(buf+strlen(buf), "%s Mbit, %s duplex, ",
265                 ((bmcr2 & (MII_BMCR2_1000HALF | MII_BMCR2_1000FULL)) & lpa2 >> 2)
266                 ? "1000"
267                 : (bmcr & MII_BMCR_100MBIT) ? "100" : "10",
268                 (bmcr & MII_BMCR_DUPLEX) ? "full" : "half");
269     }
270     strcat(buf, (bmsr & MII_BMSR_LINK_VALID) ? "link ok" : "no link");
271
272     if (opt_watch) {
273         if (opt_log) {
274             syslog(LOG_INFO, buf);
275         } else {
276             char s[20];
277             time_t t = time(NULL);
278             strftime(s, sizeof(s), "%T", localtime(&t));
279             printf("%s %s\n", s, buf);
280         }
281     } else {
282         printf("%s\n", buf);
283     }
284
285     if (verbose > 1) {
286         printf("  registers for MII PHY %d: ", phy_id);
287         for (i = 0; i < 32; i++)
288             printf("%s %4.4x", ((i % 8) ? "" : "\n   "), mii_val[i]);
289         printf("\n");
290     }
291
292     if (verbose) {
293         printf("  product info: ");
294         for (i = 0; i < NMII; i++)
295             if ((mii_id[i].id1 == mii_val[2]) &&
296                 (mii_id[i].id2 == (mii_val[3] & 0xfff0)))
297                 break;
298         if (i < NMII)
299             printf("%s rev %d\n", mii_id[i].name, mii_val[3]&0x0f);
300         else
301             printf("vendor %02x:%02x:%02x, model %d rev %d\n",
302                    mii_val[2]>>10, (mii_val[2]>>2)&0xff,
303                    ((mii_val[2]<<6)|(mii_val[3]>>10))&0xff,
304                    (mii_val[3]>>4)&0x3f, mii_val[3]&0x0f);
305         printf("  basic mode:   ");
306         if (bmcr & MII_BMCR_RESET)
307             printf("software reset, ");
308         if (bmcr & MII_BMCR_LOOPBACK)
309             printf("loopback, ");
310         if (bmcr & MII_BMCR_ISOLATE)
311             printf("isolate, ");
312         if (bmcr & MII_BMCR_COLTEST)
313             printf("collision test, ");
314         if (bmcr & MII_BMCR_AN_ENA) {
315             printf("autonegotiation enabled\n");
316         } else {
317             printf("%s Mbit, %s duplex\n",
318                    (bmcr & MII_BMCR_100MBIT) ? "100" : "10",
319                    (bmcr & MII_BMCR_DUPLEX) ? "full" : "half");
320         }
321         printf("  basic status: ");
322         if (bmsr & MII_BMSR_AN_COMPLETE)
323             printf("autonegotiation complete, ");
324         else if (bmcr & MII_BMCR_RESTART)
325             printf("autonegotiation restarted, ");
326         if (bmsr & MII_BMSR_REMOTE_FAULT)
327             printf("remote fault, ");
328         printf((bmsr & MII_BMSR_LINK_VALID) ? "link ok" : "no link");
329         printf("\n  capabilities:%s", media_list(bmsr >> 6, bmcr2, 0));
330         printf("\n  advertising: %s", media_list(advert, lpa2 >> 2, 0));
331         if (lkpar & MII_AN_ABILITY_MASK)
332             printf("\n  link partner:%s", media_list(lkpar, bmcr2, 0));
333         printf("\n");
334     }
335     fflush(stdout);
336     return 0;
337 }
338
339 /*--------------------------------------------------------------------*/
340
341 static int do_one_xcvr(int skfd, char *ifname, int maybe)
342 {
343     struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
344
345     /* Get the vitals from the interface. */
346     strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
347     if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
348         if (!maybe || (errno != ENODEV))
349             fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
350                     ifname, strerror(errno));
351         return 1;
352     }
353
354     if (override_phy >= 0) {
355         printf("using the specified MII index %d.\n", override_phy);
356         mii->phy_id = override_phy;
357     }
358
359     if (opt_reset) {
360         printf("resetting the transceiver...\n");
361         mdio_write(skfd, MII_BMCR, MII_BMCR_RESET);
362     }
363     if (nway_advertise > 0) {
364         mdio_write(skfd, MII_ANAR, nway_advertise | 1);
365         opt_restart = 1;
366     }
367     if (opt_restart) {
368         printf("restarting autonegotiation...\n");
369         mdio_write(skfd, MII_BMCR, 0x0000);
370         mdio_write(skfd, MII_BMCR, MII_BMCR_AN_ENA|MII_BMCR_RESTART);
371     }
372     if (fixed_speed) {
373         int bmcr = 0;
374         if (fixed_speed & (MII_AN_100BASETX_FD|MII_AN_100BASETX_HD))
375             bmcr |= MII_BMCR_100MBIT;
376         if (fixed_speed & (MII_AN_100BASETX_FD|MII_AN_10BASET_FD))
377             bmcr |= MII_BMCR_DUPLEX;
378         mdio_write(skfd, MII_BMCR, bmcr);
379     }
380
381     if (!opt_restart && !opt_reset && !fixed_speed && !nway_advertise)
382         show_basic_mii(skfd, mii->phy_id);
383
384     return 0;
385 }
386
387 /*--------------------------------------------------------------------*/
388
389 static void watch_one_xcvr(int skfd, char *ifname, int index)
390 {
391     struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
392     static int status[MAX_ETH] = { 0, /* ... */ };
393     int now;
394
395     /* Get the vitals from the interface. */
396     strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
397     if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
398         if (errno != ENODEV)
399             fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
400                     ifname, strerror(errno));
401         return;
402     }
403     now = (mdio_read(skfd, MII_BMCR) |
404            (mdio_read(skfd, MII_BMSR) << 16));
405     if (status[index] && (status[index] != now))
406         show_basic_mii(skfd, mii->phy_id);
407     status[index] = now;
408 }
409
410 /*--------------------------------------------------------------------*/
411
412 const char *usage =
413 "usage: %s [-VvRrwl] [-A media,... | -F media] [interface ...]\n"
414 "       -V, --version               display version information\n"
415 "       -v, --verbose               more verbose output\n"
416 "       -R, --reset                 reset MII to poweron state\n"
417 "       -r, --restart               restart autonegotiation\n"
418 "       -w, --watch                 monitor for link status changes\n"
419 "       -l, --log                   with -w, write events to syslog\n"
420 "       -A, --advertise=media,...   advertise only specified media\n"
421 "       -F, --force=media           force specified media technology\n"
422 "media: 1000baseTx-HD, 1000baseTx-FD,\n"
423 "       100baseT4, 100baseTx-FD, 100baseTx-HD,\n"
424 "       10baseT-FD, 10baseT-HD,\n"
425 "       (to advertise both HD and FD) 1000baseTx, 100baseTx, 10baseT\n";
426
427
428 static void version(void)
429 {
430     fprintf(stderr, "%s\n%s\n", Version, RELEASE);
431     exit(5); /* E_VERSION */
432 }
433
434
435 int main(int argc, char **argv)
436 {
437     int i, c, ret, errflag = 0;
438     char s[6];
439     unsigned ctrl1000 = 0;
440     
441     while ((c = getopt_long(argc, argv, "A:F:p:lrRvVw?", longopts, 0)) != EOF)
442         switch (c) {
443         case 'A': nway_advertise = parse_media(optarg, &ctrl1000); break;
444         case 'F': fixed_speed = parse_media(optarg, &ctrl1000); break;
445         case 'p': override_phy = atoi(optarg); break;
446         case 'r': opt_restart++;        break;
447         case 'R': opt_reset++;          break;
448         case 'v': verbose++;            break;
449         case 'V': opt_version++;        break;
450         case 'w': opt_watch++;          break;
451         case 'l': opt_log++;            break;
452         case '?': errflag++;
453         }
454     /* Check for a few inappropriate option combinations */
455     if (opt_watch) verbose = 0;
456
457     if ((nway_advertise < 0) || (fixed_speed < 0))
458         return 2;
459
460     if (errflag || (fixed_speed & (fixed_speed-1)) ||
461         (fixed_speed && (opt_restart || nway_advertise))) {
462         fprintf(stderr, usage, argv[0]);
463         return 2;
464     }
465
466     if (opt_version)
467         version();
468
469     /* Open a basic socket. */
470     if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
471         perror("socket");
472         exit(-1);
473     }
474
475     if (verbose > 1)
476         printf("Using SIOCGMIIPHY=0x%x\n", SIOCGMIIPHY);        
477
478     /* No remaining args means show all interfaces. */
479     if (optind == argc) {
480         ret = 1;
481         for (i = 0; i < MAX_ETH; i++) {
482             sprintf(s, "eth%d", i);
483             ret &= do_one_xcvr(skfd, s, 1);
484         }
485         if (ret)
486             fprintf(stderr, "no MII interfaces found\n");
487     } else {
488         ret = 0;
489         for (i = optind; i < argc; i++) {
490             ret |= do_one_xcvr(skfd, argv[i], 0);
491         }
492     }
493
494     if (opt_watch && (ret == 0)) {
495         while (1) {
496             sleep(1);
497             if (optind == argc) {
498                 for (i = 0; i < MAX_ETH; i++) {
499                     sprintf(s, "eth%d", i);
500                     watch_one_xcvr(skfd, s, i);
501                 }
502             } else {
503                 for (i = optind; i < argc; i++)
504                     watch_one_xcvr(skfd, argv[i], i-optind);
505             }
506         }
507     }
508
509     close(skfd);
510     return ret;
511 }