mii-tool: use <linux/mii.h> instead of "mii.h" and fix RHBZ #491358
[platform/upstream/net-tools.git] / 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 <linux/mii.h>
55 #include <linux/sockios.h>
56 #include "version.h"
57
58 #define MAX_ETH         8               /* Maximum # of interfaces */
59 #define LPA_ABILITY_MASK        0x07e0
60
61 /* Table of known MII's */
62 static const struct {
63     u_short     id1, id2;
64     char        *name;
65 } mii_id[] = {
66     { 0x0022, 0x5610, "AdHoc AH101LF" },
67     { 0x0022, 0x5520, "Altimata AC101LF" },
68     { 0x0000, 0x6b90, "AMD 79C901A HomePNA" },
69     { 0x0000, 0x6b70, "AMD 79C901A 10baseT" },
70     { 0x0181, 0xb800, "Davicom DM9101" },
71     { 0x0043, 0x7411, "Enable EL40-331" },
72     { 0x0015, 0xf410, "ICS 1889" },
73     { 0x0015, 0xf420, "ICS 1890" },
74     { 0x0015, 0xf430, "ICS 1892" },
75     { 0x02a8, 0x0150, "Intel 82555" },
76     { 0x7810, 0x0000, "Level One LXT970/971" },
77     { 0x2000, 0x5c00, "National DP83840A" },
78     { 0x0181, 0x4410, "Quality QS6612" },
79     { 0x0282, 0x1c50, "SMSC 83C180" },
80     { 0x0300, 0xe540, "TDK 78Q2120" },
81     { 0x0141, 0x0c20, "Yukon 88E1011" },
82     { 0x0141, 0x0cc0, "Yukon-EC 88E1111" },
83     { 0x0141, 0x0c90, "Yukon-2 88E1112" },
84 };
85 #define NMII (sizeof(mii_id)/sizeof(mii_id[0]))
86
87 /*--------------------------------------------------------------------*/
88
89 struct option longopts[] = {
90  /* { name  has_arg  *flag  val } */
91     {"advertise",       1, 0, 'A'},     /* Change capabilities advertised. */
92     {"force",           1, 0, 'F'},     /* Change capabilities advertised. */
93     {"phy",             1, 0, 'p'},     /* Set PHY (MII address) to report. */
94     {"log",             0, 0, 'l'},     /* Set PHY (MII address) to report. */
95     {"restart",         0, 0, 'r'},     /* Restart link negotiation */
96     {"reset",           0, 0, 'R'},     /* Reset the transceiver. */
97     {"verbose",         0, 0, 'v'},     /* Report each action taken.  */
98     {"version",         0, 0, 'V'},     /* Emit version information.  */
99     {"watch",           0, 0, 'w'},     /* Constantly monitor the port.  */
100     {"help",            0, 0, '?'},     /* Give help */
101     { 0, 0, 0, 0 }
102 };
103
104 static unsigned int
105     verbose = 0,
106     opt_version = 0,
107     opt_restart = 0,
108     opt_reset = 0,
109     opt_log = 0,
110     opt_watch = 0;
111 static int nway_advertise = 0;
112 static int fixed_speed = 0;
113 static int override_phy = -1;
114
115 static int skfd = -1;           /* AF_INET socket for ioctl() calls. */
116 static struct ifreq ifr;
117
118 /*--------------------------------------------------------------------*/
119
120 static int mdio_read(int skfd, int location)
121 {
122     struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
123     mii->reg_num = location;
124     if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) {
125         fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,
126                 strerror(errno));
127         return -1;
128     }
129     return mii->val_out;
130 }
131
132 static void mdio_write(int skfd, int location, int value)
133 {
134     struct mii_ioctl_data *mii = (struct mii_ioctl_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));
140     }
141 }
142
143 /*--------------------------------------------------------------------*/
144
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",     {LPA_10HALF} },
151     { "10baseT-FD",     {LPA_10FULL} },
152     { "100baseTx-HD",   {LPA_100HALF} },
153     { "100baseTx-FD",   {LPA_100FULL} },
154     { "100baseT4",      {LPA_100BASE4} },
155     { "100baseTx",      {LPA_100FULL | LPA_100HALF} },
156     { "10baseT",        {LPA_10FULL | LPA_10HALF} },
157
158     { "1000baseT-HD",   {0, ADVERTISE_1000HALF} },
159     { "1000baseT-FD",   {0, ADVERTISE_1000FULL} },
160     { "1000baseT",      {0, ADVERTISE_1000HALF|ADVERTISE_1000FULL} },
161 };
162 #define NMEDIA (sizeof(media)/sizeof(media[0]))
163         
164 /* Parse an argument list of media types */
165 static int parse_media(char *arg, unsigned *bmcr2)
166 {
167     int mask, i;
168     char *s;
169     mask = strtoul(arg, &s, 16);
170     if ((*arg != '\0') && (*s == '\0')) {
171         if ((mask & LPA_ABILITY_MASK) &&
172             !(mask & ~LPA_ABILITY_MASK)) {
173                 *bmcr2 = 0;
174                 return mask;
175         }
176         goto failed;
177     }
178     mask = 0;
179     *bmcr2 = 0;
180     s = strtok(arg, ", ");
181     do {
182             for (i = 0; i < NMEDIA; i++)
183                 if (s && strcasecmp(media[i].name, s) == 0) break;
184             if (i == NMEDIA) goto failed;
185             mask |= media[i].value[0];
186             *bmcr2 |= media[i].value[1];
187     } while ((s = strtok(NULL, ", ")) != NULL);
188
189     return mask;
190 failed:
191     fprintf(stderr, "Invalid media specification '%s'.\n", arg);
192     return -1;
193 }
194
195 /*--------------------------------------------------------------------*/
196
197 static const char *media_list(unsigned mask, unsigned mask2, int best)
198 {
199     static char buf[100];
200     int i;
201     *buf = '\0';
202
203     if (mask & BMCR_SPEED1000) {
204         if (mask2 & ADVERTISE_1000HALF) {
205             strcat(buf, " ");
206             strcat(buf, "1000baseT-HD");
207             if (best) goto out;
208         }
209         if (mask2 & ADVERTISE_1000FULL) {
210             strcat(buf, " ");
211             strcat(buf, "1000baseT-FD");
212             if (best) goto out;
213         }
214     }
215
216     mask >>= 5;
217     for (i = 4; i >= 0; i--) {
218         if (mask & (1<<i)) {
219             strcat(buf, " ");
220             strcat(buf, media[i].name);
221             if (best) break;
222         }
223     }
224  out:
225     if (mask & (1<<5))
226         strcat(buf, " flow-control");
227     return buf;
228 }
229
230 int show_basic_mii(int sock, int phy_id)
231 {
232     char buf[100];
233     int i, mii_val[32];
234     unsigned bmcr, bmsr, advert, lkpar, bmcr2, lpa2;
235
236     /* Some bits in the BMSR are latched, but we can't rely on being
237        the only reader, so only the current values are meaningful */
238     mdio_read(sock, MII_BMSR);
239     for (i = 0; i < ((verbose > 1) ? 32 : (MII_STAT1000+1)); i++)
240         switch (i & 0x1F) {
241             case MII_BMCR:
242             case MII_BMSR:
243             case MII_PHYSID1:
244             case MII_PHYSID2:
245             case MII_ADVERTISE:
246             case MII_LPA:
247             case MII_EXPANSION:
248             case MII_CTRL1000:
249             case MII_STAT1000:
250             case MII_ESTATUS:
251             case MII_DCOUNTER:
252             case MII_FCSCOUNTER:
253             case MII_NWAYTEST:
254             case MII_RERRCOUNTER:
255             case MII_SREVISION:
256             case MII_RESV1:
257             case MII_LBRERROR:
258             case MII_PHYADDR:
259             case MII_RESV2:
260             case MII_TPISTATUS:
261             case MII_NCONFIG:
262                 mii_val[i] = mdio_read(sock, i);
263                 break;
264             default:
265                 mii_val[i] = 0;
266                 break;
267         }
268
269     if (mii_val[MII_BMCR] == 0xffff  || mii_val[MII_BMSR] == 0x0000) {
270         fprintf(stderr, "  No MII transceiver present!.\n");
271         return -1;
272     }
273
274     /* Descriptive rename. */
275     bmcr = mii_val[MII_BMCR]; bmsr = mii_val[MII_BMSR];
276     advert = mii_val[MII_ADVERTISE]; lkpar = mii_val[MII_LPA];
277     bmcr2 = mii_val[MII_CTRL1000]; lpa2 = mii_val[MII_STAT1000];
278
279     sprintf(buf, "%s: ", ifr.ifr_name);
280     if (bmcr & BMCR_ANENABLE) {
281         if (bmsr & BMSR_ANEGCOMPLETE) {
282             if (advert & lkpar) {
283                 strcat(buf, (lkpar & LPA_LPACK) ?
284                        "negotiated" : "no autonegotiation,");
285                 strcat(buf, media_list(advert & lkpar, bmcr2 & lpa2>>2, 1));
286                 strcat(buf, ", ");
287             } else {
288                 strcat(buf, "autonegotiation failed, ");
289             }
290         } else if (bmcr & BMCR_ANRESTART) {
291             strcat(buf, "autonegotiation restarted, ");
292         }
293     } else {
294         sprintf(buf+strlen(buf), "%s Mbit, %s duplex, ",
295                 ((bmcr2 & (ADVERTISE_1000HALF | ADVERTISE_1000FULL)) & lpa2 >> 2)
296                 ? "1000"
297                 : (bmcr & BMCR_SPEED100) ? "100" : "10",
298                 (bmcr & BMCR_FULLDPLX) ? "full" : "half");
299     }
300     strcat(buf, (bmsr & BMSR_LSTATUS) ? "link ok" : "no link");
301
302     if (opt_watch) {
303         if (opt_log) {
304             syslog(LOG_INFO, "%s", buf);
305         } else {
306             char s[20];
307             time_t t = time(NULL);
308             strftime(s, sizeof(s), "%T", localtime(&t));
309             printf("%s %s\n", s, buf);
310         }
311     } else {
312         printf("%s\n", buf);
313     }
314
315     if (verbose > 1) {
316         printf("  registers for MII PHY %d: ", phy_id);
317         for (i = 0; i < 32; i++)
318             printf("%s %4.4x", ((i % 8) ? "" : "\n   "), mii_val[i]);
319         printf("\n");
320     }
321
322     if (verbose) {
323         printf("  product info: ");
324         for (i = 0; i < NMII; i++)
325             if ((mii_id[i].id1 == mii_val[2]) &&
326                 (mii_id[i].id2 == (mii_val[3] & 0xfff0)))
327                 break;
328         if (i < NMII)
329             printf("%s rev %d\n", mii_id[i].name, mii_val[3]&0x0f);
330         else
331             printf("vendor %02x:%02x:%02x, model %d rev %d\n",
332                    mii_val[2]>>10, (mii_val[2]>>2)&0xff,
333                    ((mii_val[2]<<6)|(mii_val[3]>>10))&0xff,
334                    (mii_val[3]>>4)&0x3f, mii_val[3]&0x0f);
335         printf("  basic mode:   ");
336         if (bmcr & BMCR_RESET)
337             printf("software reset, ");
338         if (bmcr & BMCR_LOOPBACK)
339             printf("loopback, ");
340         if (bmcr & BMCR_ISOLATE)
341             printf("isolate, ");
342         if (bmcr & BMCR_CTST)
343             printf("collision test, ");
344         if (bmcr & BMCR_ANENABLE) {
345             printf("autonegotiation enabled\n");
346         } else {
347             printf("%s Mbit, %s duplex\n",
348                    (bmcr & BMCR_SPEED100) ? "100" : "10",
349                    (bmcr & BMCR_FULLDPLX) ? "full" : "half");
350         }
351         printf("  basic status: ");
352         if (bmsr & BMSR_ANEGCOMPLETE)
353             printf("autonegotiation complete, ");
354         else if (bmcr & BMCR_ANRESTART)
355             printf("autonegotiation restarted, ");
356         if (bmsr & BMSR_RFAULT)
357             printf("remote fault, ");
358         printf((bmsr & BMSR_LSTATUS) ? "link ok" : "no link");
359         printf("\n  capabilities:%s", media_list(bmsr >> 6, bmcr2, 0));
360         printf("\n  advertising: %s", media_list(advert, bmcr2, 0));
361         if (lkpar & LPA_ABILITY_MASK)
362             printf("\n  link partner:%s", media_list(lkpar, lpa2 >> 2, 0));
363         printf("\n");
364     }
365     fflush(stdout);
366     return 0;
367 }
368
369 /*--------------------------------------------------------------------*/
370
371 static int do_one_xcvr(int skfd, char *ifname, int maybe)
372 {
373     struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
374
375     /* Get the vitals from the interface. */
376     strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
377     if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
378         if (!maybe || (errno != ENODEV))
379             fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
380                     ifname, strerror(errno));
381         return 1;
382     }
383
384     if (override_phy >= 0) {
385         printf("using the specified MII index %d.\n", override_phy);
386         mii->phy_id = override_phy;
387     }
388
389     if (opt_reset) {
390         printf("resetting the transceiver...\n");
391         mdio_write(skfd, MII_BMCR, BMCR_RESET);
392     }
393     if (nway_advertise > 0) {
394         mdio_write(skfd, MII_ADVERTISE, nway_advertise | 1);
395         opt_restart = 1;
396     }
397     if (opt_restart) {
398         printf("restarting autonegotiation...\n");
399         mdio_write(skfd, MII_BMCR, 0x0000);
400         mdio_write(skfd, MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
401     }
402     if (fixed_speed) {
403         int bmcr = 0;
404         if (fixed_speed & (LPA_100FULL|LPA_100HALF))
405             bmcr |= BMCR_SPEED100;
406         if (fixed_speed & (LPA_100FULL|LPA_10FULL))
407             bmcr |= BMCR_FULLDPLX;
408         mdio_write(skfd, MII_BMCR, bmcr);
409     }
410
411     if (!opt_restart && !opt_reset && !fixed_speed && !nway_advertise)
412         show_basic_mii(skfd, mii->phy_id);
413
414     return 0;
415 }
416
417 /*--------------------------------------------------------------------*/
418
419 static void watch_one_xcvr(int skfd, char *ifname, int index)
420 {
421     struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&ifr.ifr_data;
422     static int status[MAX_ETH] = { 0, /* ... */ };
423     int now;
424
425     /* Get the vitals from the interface. */
426     strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
427     if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
428         if (errno != ENODEV)
429             fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
430                     ifname, strerror(errno));
431         return;
432     }
433     now = (mdio_read(skfd, MII_BMCR) |
434            (mdio_read(skfd, MII_BMSR) << 16));
435     if (status[index] && (status[index] != now))
436         show_basic_mii(skfd, mii->phy_id);
437     status[index] = now;
438 }
439
440 /*--------------------------------------------------------------------*/
441
442 const char *usage =
443 "usage: %s [-VvRrwl] [-A media,... | -F media] [interface ...]\n"
444 "       -V, --version               display version information\n"
445 "       -v, --verbose               more verbose output\n"
446 "       -R, --reset                 reset MII to poweron state\n"
447 "       -r, --restart               restart autonegotiation\n"
448 "       -w, --watch                 monitor for link status changes\n"
449 "       -l, --log                   with -w, write events to syslog\n"
450 "       -A, --advertise=media,...   advertise only specified media\n"
451 "       -F, --force=media           force specified media technology\n"
452 "media: 1000baseTx-HD, 1000baseTx-FD,\n"
453 "       100baseT4, 100baseTx-FD, 100baseTx-HD,\n"
454 "       10baseT-FD, 10baseT-HD,\n"
455 "       (to advertise both HD and FD) 1000baseTx, 100baseTx, 10baseT\n";
456
457
458 static void version(void)
459 {
460     fprintf(stderr, "%s\n%s\n", Version, RELEASE);
461     exit(5); /* E_VERSION */
462 }
463
464
465 int main(int argc, char **argv)
466 {
467     int i, c, ret, errflag = 0;
468     char s[6];
469     unsigned ctrl1000 = 0;
470     
471     while ((c = getopt_long(argc, argv, "A:F:p:lrRvVw?", longopts, 0)) != EOF)
472         switch (c) {
473         case 'A': nway_advertise = parse_media(optarg, &ctrl1000); break;
474         case 'F': fixed_speed = parse_media(optarg, &ctrl1000); break;
475         case 'p': override_phy = atoi(optarg); break;
476         case 'r': opt_restart++;        break;
477         case 'R': opt_reset++;          break;
478         case 'v': verbose++;            break;
479         case 'V': opt_version++;        break;
480         case 'w': opt_watch++;          break;
481         case 'l': opt_log++;            break;
482         case '?': errflag++;
483         }
484     /* Check for a few inappropriate option combinations */
485     if (opt_watch) verbose = 0;
486
487     if ((nway_advertise < 0) || (fixed_speed < 0))
488         return 2;
489
490     if (errflag || (fixed_speed & (fixed_speed-1)) ||
491         (fixed_speed && (opt_restart || nway_advertise))) {
492         fprintf(stderr, usage, argv[0]);
493         return 2;
494     }
495
496     if (opt_version)
497         version();
498
499     /* Open a basic socket. */
500     if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
501         perror("socket");
502         exit(-1);
503     }
504
505     if (verbose > 1)
506         printf("Using SIOCGMIIPHY=0x%x\n", SIOCGMIIPHY);        
507
508     /* No remaining args means show all interfaces. */
509     if (optind == argc) {
510         ret = 1;
511         for (i = 0; i < MAX_ETH; i++) {
512             sprintf(s, "eth%d", i);
513             ret &= do_one_xcvr(skfd, s, 1);
514         }
515         if (ret)
516             fprintf(stderr, "no MII interfaces found\n");
517     } else {
518         ret = 0;
519         for (i = optind; i < argc; i++) {
520             ret |= do_one_xcvr(skfd, argv[i], 0);
521         }
522     }
523
524     if (opt_watch && (ret == 0)) {
525         while (1) {
526             sleep(1);
527             if (optind == argc) {
528                 for (i = 0; i < MAX_ETH; i++) {
529                     sprintf(s, "eth%d", i);
530                     watch_one_xcvr(skfd, s, i);
531                 }
532             } else {
533                 for (i = optind; i < argc; i++)
534                     watch_one_xcvr(skfd, argv[i], i-optind);
535             }
536         }
537     }
538
539     close(skfd);
540     return ret;
541 }