Add mii-tool from David Hinds.
authorPhil Blundell <philb@gnu.org>
Sun, 21 May 2000 13:44:36 +0000 (13:44 +0000)
committerPhil Blundell <philb@gnu.org>
Sun, 21 May 2000 13:44:36 +0000 (13:44 +0000)
Makefile
config.in
include/mii.h [new file with mode: 0644]
man/en_US/mii-tool.8 [new file with mode: 0644]
mii-tool.c [new file with mode: 0644]

index 56199fb..850d297 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -82,6 +82,9 @@ PROGS := ifconfig hostname arp netstat route rarp slattach plipconfig
 ifeq ($(HAVE_IP_TOOLS),1)
 PROGS   += iptunnel ipmaddr
 endif
+ifeq ($(HAVE_MII),1)
+PROGS  += mii-tool
+endif
 
 # Compiler and Linker Options
 # You may need to uncomment and edit these if you are using libc5 and IPv6.
@@ -211,6 +214,9 @@ iptunnel:   $(NET-LIB) iptunnel.o
 ipmaddr:       $(NET-LIB) ipmaddr.o
                $(CC) $(LDFLAGS) -o ipmaddr ipmaddr.o $(NLIB) $(RESLIB)
 
+mii-tool:      mii-tool.o
+               $(CC) $(LDFLAGS) -o mii-tool mii-tool.o
+
 installbin:
        install -m 0755 -d ${BASEDIR}/sbin
        install -m 0755 -d ${BASEDIR}/bin
@@ -226,6 +232,9 @@ ifeq ($(HAVE_IP_TOOLS),1)
        install -m 0755 ipmaddr    $(BASEDIR)/sbin
        install -m 0755 iptunnel   $(BASEDIR)/sbin
 endif
+ifeq ($(HAVE_MII),1)
+       install -m 0755 mii-tool   $(BASEDIR)/sbin
+endif
        ln -fs hostname $(BASEDIR)/bin/dnsdomainname
        ln -fs hostname $(BASEDIR)/bin/ypdomainname
        ln -fs hostname $(BASEDIR)/bin/nisdomainname
index 43c1ae1..f3310d6 100644 (file)
--- a/config.in
+++ b/config.in
@@ -88,3 +88,4 @@ bool 'Econet hardware support' HAVE_HWEC n
 *
 bool 'IP Masquerading support' HAVE_FW_MASQUERADE n
 bool 'Build iptunnel and ipmaddr' HAVE_IP_TOOLS n
+bool 'Build mii-tool' HAVE_MII n
diff --git a/include/mii.h b/include/mii.h
new file mode 100644 (file)
index 0000000..1ef7ccc
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * mii.h 1.4 2000/04/25 22:06:15
+ *
+ * Media Independent Interface support: register layout and ioctl's
+ *
+ * Copyright (C) 2000 David A. Hinds -- dhinds@pcmcia.sourceforge.org
+ */
+
+#ifndef _LINUX_MII_H
+#define _LINUX_MII_H
+
+/* network interface ioctl's for MII commands */
+#ifndef SIOCGMIIPHY
+#define SIOCGMIIPHY (SIOCDEVPRIVATE)   /* Read from current PHY */
+#define SIOCGMIIREG (SIOCDEVPRIVATE+1)         /* Read any PHY register */
+#define SIOCSMIIREG (SIOCDEVPRIVATE+2)         /* Write any PHY register */
+#define SIOCGPARAMS (SIOCDEVPRIVATE+3)         /* Read operational parameters */
+#define SIOCSPARAMS (SIOCDEVPRIVATE+4)         /* Set operational parameters */
+#endif
+
+#include <linux/types.h>
+
+/* This data structure is used for all the MII ioctl's */
+struct mii_data {
+    __u16      phy_id;
+    __u16      reg_num;
+    __u16      val_in;
+    __u16      val_out;
+};
+
+/* Basic Mode Control Register */
+#define MII_BMCR               0x00
+#define  MII_BMCR_RESET                0x8000
+#define  MII_BMCR_LOOPBACK     0x4000
+#define  MII_BMCR_100MBIT      0x2000
+#define  MII_BMCR_AN_ENA       0x1000
+#define  MII_BMCR_ISOLATE      0x0400
+#define  MII_BMCR_RESTART      0x0200
+#define  MII_BMCR_DUPLEX       0x0100
+#define  MII_BMCR_COLTEST      0x0080
+
+/* Basic Mode Status Register */
+#define MII_BMSR               0x01
+#define  MII_BMSR_CAP_MASK     0xf800
+#define  MII_BMSR_100BASET4    0x8000
+#define  MII_BMSR_100BASETX_FD 0x4000
+#define  MII_BMSR_100BASETX_HD 0x2000
+#define  MII_BMSR_10BASET_FD   0x1000
+#define  MII_BMSR_10BASET_HD   0x0800
+#define  MII_BMSR_NO_PREAMBLE  0x0040
+#define  MII_BMSR_AN_COMPLETE  0x0020
+#define  MII_BMSR_REMOTE_FAULT 0x0010
+#define  MII_BMSR_AN_ABLE      0x0008
+#define  MII_BMSR_LINK_VALID   0x0004
+#define  MII_BMSR_JABBER       0x0002
+#define  MII_BMSR_EXT_CAP      0x0001
+
+#define MII_PHY_ID1            0x02
+#define MII_PHY_ID2            0x03
+
+/* Auto-Negotiation Advertisement Register */
+#define MII_ANAR               0x04
+/* Auto-Negotiation Link Partner Ability Register */
+#define MII_ANLPAR             0x05
+#define  MII_AN_NEXT_PAGE      0x8000
+#define  MII_AN_ACK            0x4000
+#define  MII_AN_REMOTE_FAULT   0x2000
+#define  MII_AN_ABILITY_MASK   0x07e0
+#define  MII_AN_FLOW_CONTROL   0x0400
+#define  MII_AN_100BASET4      0x0200
+#define  MII_AN_100BASETX_FD   0x0100
+#define  MII_AN_100BASETX_HD   0x0080
+#define  MII_AN_10BASET_FD     0x0040
+#define  MII_AN_10BASET_HD     0x0020
+#define  MII_AN_PROT_MASK      0x001f
+#define  MII_AN_PROT_802_3     0x0001
+
+/* Auto-Negotiation Expansion Register */
+#define MII_ANER               0x06
+#define  MII_ANER_MULT_FAULT   0x0010
+#define  MII_ANER_LP_NP_ABLE   0x0008
+#define  MII_ANER_NP_ABLE      0x0004
+#define  MII_ANER_PAGE_RX      0x0002
+#define  MII_ANER_LP_AN_ABLE   0x0001
+
+#endif /* _LINUX_MII_H */
diff --git a/man/en_US/mii-tool.8 b/man/en_US/mii-tool.8
new file mode 100644 (file)
index 0000000..6b773ec
--- /dev/null
@@ -0,0 +1,78 @@
+.\" Copyright (C) 2000 David A. Hinds -- dhinds@pcmcia.sourceforge.org
+.\" mii-tool.8 1.5 2000/04/25 22:58:19
+.\"
+.TH MII-TOOL 8 "2000/04/25 22:58:19" "net-tools"
+
+.SH NAME
+mii-tool \- view, manipulate media-independent interface status
+
+.SH SYNOPSIS
+.B mii-tool
+[\fB\-v\fR, \fB\-\-verbose\fR]
+[\fB\-V\fR, \fB\-\-version\fR]
+[\fB\-R\fR, \fB\-\-reset\fR]
+[\fB\-r\fR, \fB\-\-restart\fR]
+[\fB\-w\fR, \fB\-\-watch\fR]
+[\fB\-l\fR, \fB\-\-log\fR]
+[\fB\-A\fR, \fB\-\-advertise=\fImedia,...\fR]
+[\fB\-F\fR, \fB\-\-force=\fImedia\fR]
+.RI [ "interface\ ..." ]
+
+.SH DESCRIPTION
+This utility checks or sets the status of a network interface's Media
+Independent Interface (MII) unit.  Most fast ethernet adapters use an
+MII to autonegotiate link speed and duplex setting.
+.PP
+Most intelligent network devices use an autonegotiation protocol to
+communicate what media technologies they support, and then select the
+fastest mutually supported media technology.  The \fB\-A\fR or
+\fB\-\-advertise\fR options can be used to tell the MII to only
+advertise a subset of its capabilities.  Some passive devices, such as
+single-speed hubs, are unable to autonegotiate.  To handle such
+devices, the MII protocol also allows for establishing a link by
+simply detecting either a 10baseT or 100baseT link beat.  The \fB\-F\fR
+or \fB\-\-force\fR options can be used to force the MII to operate in
+one mode, instead of autonegotiating.  The \fB\-A\fR and \fB-F\fR
+options are mutually exclusive.
+.PP
+The default short output reports the negotiated link speed and link
+status for each interface.  If an interface or interfaces are not
+specified on the command line, then \fBmii-tool\fR will check any
+available interfaces from \fBeth0\fR through \fBeth7\fR.
+.SH OPTIONS
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Display more detailed MII status information.  If used twice, also
+display raw MII register contents.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display program version information.
+.TP
+\fB\-R\fR, \fB\-\-reset\fR
+Reset the MII to its default configuration.
+.TP
+\fB\-r\fR, \fB\-\-restart\fR
+Restart autonegotiation.
+.TP
+\fB\-w\fR, \fB\-\-watch\fR
+Watch interface(s) and report changes in link status.  The MII
+interfaces are polled at one second intervals.
+.TP
+\fB\-l\fR, \fB\-\-log\fR
+Used with \fB-w\fR, records link status changes in the system log
+instead of printing on standard output.
+.TP
+\fB\-F\fI media\fR, \fB\-\-force=\fImedia\fR
+Disable autonegotiation, and force the MII to either \fB100baseTx-FD\fR,
+\fB100baseTx-HD\fR, \fB10baseT-FD\fR, or \fB10baseT-HD\fR operation.
+.TP
+\fB\-A\fI media,...\fR, \fB\-\-advertise=\fImedia,...\fR
+Enable and restart autonegotiation, and advertise only the specified
+media technologies.  Multiple technologies should be separated by
+commas.  Valid media are \fB100baseT4\fR, \fB100baseTx-FD\fR,
+\fB100baseTx-HD\fR, \fB10baseT-FD\fR, and \fB10baseT-HD\fR.
+
+.SH AUTHORS
+David Hinds \- dhinds@pcmcia.sourceforge.org
+.br
+Donald Becker \- becker@scyld.com
diff --git a/mii-tool.c b/mii-tool.c
new file mode 100644 (file)
index 0000000..ef2d48a
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+
+    mii-tool: monitor and control the MII for a network interface
+
+    Usage:
+
+       mii-tool [-VvRrw] [-A media,... | -F media] [interface ...]
+
+    This program is based on Donald Becker's "mii-diag" program, which
+    is more capable and verbose than this tool, but also somewhat
+    harder to use.
+
+    Copyright (C) 2000 David A. Hinds -- dhinds@pcmcia.sourceforge.org
+
+    mii-diag is written/copyright 1997-2000 by Donald Becker
+        <becker@scyld.com>
+
+    This program is free software; you can redistribute it
+    and/or modify it under the terms of the GNU General Public
+    License as published by the Free Software Foundation.
+
+    Donald Becker may be reached as becker@scyld.com, or C/O
+    Scyld Computing Corporation, 410 Severn Av., Suite 210,
+    Annapolis, MD 21403
+
+    References
+       http://www.scyld.com/diag/mii-status.html
+       http://www.scyld.com/expert/NWay.html
+       http://www.national.com/pf/DP/DP83840.html
+*/
+
+static char version[] =
+"mii-tool.c 1.9 2000/04/28 00:56:08 (David Hinds)\n";
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <time.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifndef __GLIBC__
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#endif
+#include "mii.h"
+
+#define MAX_ETH                8               /* Maximum # of interfaces */
+
+/* Table of known MII's */
+static struct {
+    u_short    id1, id2;
+    char       *name;
+} mii_id[] = {
+    { 0x0022, 0x5610, "AdHoc AH101LF" },
+    { 0x0022, 0x5520, "Altimata AC101LF" },
+    { 0x0000, 0x6b90, "AMD 79C901A HomePNA" },
+    { 0x0000, 0x6b70, "AMD 79C901A 10baseT" },
+    { 0x0181, 0xb800, "Davicom DM9101" },
+    { 0x0043, 0x7411, "Enable EL40-331" },
+    { 0x0015, 0xf410, "ICS 1889" },
+    { 0x0015, 0xf420, "ICS 1890" },
+    { 0x0015, 0xf430, "ICS 1892" },
+    { 0x02a8, 0x0150, "Intel 82555" },
+    { 0x7810, 0x0000, "Level One LXT970/971" },
+    { 0x2000, 0x5c00, "National DP83840A" },
+    { 0x0181, 0x4410, "Quality QS6612" },
+    { 0x0282, 0x1c50, "SMSC 83C180" },
+    { 0x0300, 0xe540, "TDK 78Q2120" },
+};
+#define NMII (sizeof(mii_id)/sizeof(mii_id[0]))
+
+/*--------------------------------------------------------------------*/
+
+struct option longopts[] = {
+ /* { name  has_arg  *flag  val } */
+    {"advertise",      1, 0, 'A'},     /* Change capabilities advertised. */
+    {"force",          1, 0, 'F'},     /* Change capabilities advertised. */
+    {"phy",            1, 0, 'p'},     /* Set PHY (MII address) to report. */
+    {"log",            0, 0, 'l'},     /* Set PHY (MII address) to report. */
+    {"restart",                0, 0, 'r'},     /* Restart link negotiation */
+    {"reset",          0, 0, 'R'},     /* Reset the transceiver. */
+    {"verbose",        0, 0, 'v'},     /* Report each action taken.  */
+    {"version",        0, 0, 'V'},     /* Emit version information.  */
+    {"watch",          0, 0, 'w'},     /* Constantly monitor the port.  */
+    {"help",           0, 0, '?'},     /* Give help */
+    { 0, 0, 0, 0 }
+};
+
+static unsigned int
+    verbose = 0,
+    opt_version = 0,
+    opt_restart = 0,
+    opt_reset = 0,
+    opt_log = 0,
+    opt_watch = 0;
+static int nway_advertise = 0;
+static int fixed_speed = 0;
+static int override_phy = -1;
+
+static int skfd = -1;          /* AF_INET socket for ioctl() calls. */
+static struct ifreq ifr;
+
+/*--------------------------------------------------------------------*/
+
+static int mdio_read(int skfd, int location)
+{
+    struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
+    mii->reg_num = location;
+    if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) {
+       fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,
+               strerror(errno));
+       return -1;
+    }
+    return mii->val_out;
+}
+
+static void mdio_write(int skfd, int location, int value)
+{
+    struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
+    mii->reg_num = location;
+    mii->val_in = value;
+    if (ioctl(skfd, SIOCSMIIREG, &ifr) < 0) {
+       fprintf(stderr, "SIOCSMIIREG on %s failed: %s\n", ifr.ifr_name,
+               strerror(errno));
+    }
+}
+
+/*--------------------------------------------------------------------*/
+
+const struct {
+    char       *name;
+    u_short    value;
+} media[] = {
+    /* The order through 100baseT4 matches bits in the BMSR */
+    { "10baseT-HD",    MII_AN_10BASET_HD },
+    { "10baseT-FD",    MII_AN_10BASET_FD },
+    { "100baseTx-HD",  MII_AN_100BASETX_HD },
+    { "100baseTx-FD",  MII_AN_100BASETX_FD },
+    { "100baseT4",     MII_AN_100BASET4 },
+    { "100baseTx",     MII_AN_100BASETX_FD | MII_AN_100BASETX_HD },
+    { "10baseT",       MII_AN_10BASET_FD | MII_AN_10BASET_HD },
+};
+#define NMEDIA (sizeof(media)/sizeof(media[0]))
+       
+/* Parse an argument list of media types */
+static int parse_media(char *arg)
+{
+    int mask, i;
+    char *s;
+    mask = strtoul(arg, &s, 16);
+    if ((*arg != '\0') && (*s == '\0')) {
+       if ((mask & MII_AN_ABILITY_MASK) &&
+           !(mask & ~MII_AN_ABILITY_MASK))
+           return mask;
+       goto failed;
+    } else {
+       mask = 0;
+       s = strtok(arg, ", ");
+       do {
+           for (i = 0; i < NMEDIA; i++)
+               if (strcasecmp(media[i].name, s) == 0) break;
+           if (i == NMEDIA) goto failed;
+           mask |= media[i].value;
+       } while ((s = strtok(NULL, ", ")) != NULL);
+    }
+    return mask;
+failed:
+    fprintf(stderr, "Invalid media specification '%s'.\n", arg);
+    return -1;
+}
+
+/*--------------------------------------------------------------------*/
+
+static char *media_list(int mask, int best)
+{
+    static char buf[100];
+    int i;
+    *buf = '\0';
+    mask >>= 5;
+    for (i = 4; i >= 0; i--) {
+       if (mask & (1<<i)) {
+           strcat(buf, " ");
+           strcat(buf, media[i].name);
+           if (best) break;
+       }
+    }
+    if (mask & (1<<5))
+       strcat(buf, " flow-control");
+    return buf;
+}
+
+int show_basic_mii(int sock, int phy_id)
+{
+    char buf[100];
+    int i, mii_val[32];
+    int bmcr, bmsr, advert, lkpar;
+
+    /* Some bits in the BMSR are latched, but we can't rely on being
+       the only reader, so only the current values are meaningful */
+    mdio_read(sock, MII_BMSR);
+    for (i = 0; i < ((verbose > 1) ? 32 : 8); i++)
+       mii_val[i] = mdio_read(sock, i);
+
+    if (mii_val[MII_BMCR] == 0xffff) {
+       fprintf(stderr, "  No MII transceiver present!.\n");
+       return -1;
+    }
+
+    /* Descriptive rename. */
+    bmcr = mii_val[MII_BMCR]; bmsr = mii_val[MII_BMSR];
+    advert = mii_val[MII_ANAR]; lkpar = mii_val[MII_ANLPAR];
+
+    sprintf(buf, "%s: ", ifr.ifr_name);
+    if (bmcr & MII_BMCR_AN_ENA) {
+       if (bmsr & MII_BMSR_AN_COMPLETE) {
+           if (advert & lkpar) {
+               strcat(buf, (lkpar & MII_AN_ACK) ?
+                      "negotiated" : "no autonegotiation,");
+               strcat(buf, media_list(advert & lkpar, 1));
+               strcat(buf, ", ");
+           } else {
+               strcat(buf, "autonegotiation failed, ");
+           }
+       } else if (bmcr & MII_BMCR_RESTART) {
+           strcat(buf, "autonegotiation restarted, ");
+       }
+    } else {
+       sprintf(buf+strlen(buf), "%s Mbit, %s duplex, ",
+              (bmcr & MII_BMCR_100MBIT) ? "100" : "10",
+              (bmcr & MII_BMCR_DUPLEX) ? "full" : "half");
+    }
+    strcat(buf, (bmsr & MII_BMSR_LINK_VALID) ? "link ok" : "no link");
+
+    if (opt_watch) {
+       if (opt_log) {
+           syslog(LOG_INFO, buf);
+       } else {
+           char s[20];
+           time_t t = time(NULL);
+           strftime(s, sizeof(s), "%T", localtime(&t));
+           printf("%s %s\n", s, buf);
+       }
+    } else {
+       printf("%s\n", buf);
+    }
+
+    if (verbose > 1) {
+       printf("  registers for MII PHY %d: ", phy_id);
+       for (i = 0; i < 32; i++)
+           printf("%s %4.4x", ((i % 8) ? "" : "\n   "), mii_val[i]);
+       printf("\n");
+    }
+
+    if (verbose) {
+       printf("  product info: ");
+       for (i = 0; i < NMII; i++)
+           if ((mii_id[i].id1 == mii_val[2]) &&
+               (mii_id[i].id2 == (mii_val[3] & 0xfff0)))
+               break;
+       if (i < NMII)
+           printf("%s rev %d\n", mii_id[i].name, mii_val[3]&0x0f);
+       else
+           printf("vendor %02x:%02x:%02x, model %d rev %d\n",
+                  mii_val[2]>>10, (mii_val[2]>>2)&0xff,
+                  ((mii_val[2]<<6)|(mii_val[3]>>10))&0xff,
+                  (mii_val[3]>>4)&0x3f, mii_val[3]&0x0f);
+       printf("  basic mode:   ");
+       if (bmcr & MII_BMCR_RESET)
+           printf("software reset, ");
+       if (bmcr & MII_BMCR_LOOPBACK)
+           printf("loopback, ");
+       if (bmcr & MII_BMCR_ISOLATE)
+           printf("isolate, ");
+       if (bmcr & MII_BMCR_COLTEST)
+           printf("collision test, ");
+       if (bmcr & MII_BMCR_AN_ENA) {
+           printf("autonegotiation enabled\n");
+       } else {
+           printf("%s Mbit, %s duplex\n",
+                  (bmcr & MII_BMCR_100MBIT) ? "100" : "10",
+                  (bmcr & MII_BMCR_DUPLEX) ? "full" : "half");
+       }
+       printf("  basic status: ");
+       if (bmsr & MII_BMSR_AN_COMPLETE)
+           printf("autonegotiation complete, ");
+       else if (bmcr & MII_BMCR_RESTART)
+           printf("autonegotiation restarted, ");
+       if (bmsr & MII_BMSR_REMOTE_FAULT)
+           printf("remote fault, ");
+       printf((bmsr & MII_BMSR_LINK_VALID) ? "link ok" : "no link");
+       printf("\n  capabilities:%s", media_list(bmsr >> 6, 0));
+       printf("\n  advertising: %s", media_list(advert, 0));
+       if (lkpar & MII_AN_ABILITY_MASK)
+           printf("\n  link partner:%s", media_list(lkpar, 0));
+       printf("\n");
+    }
+    return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int do_one_xcvr(int skfd, char *ifname, int maybe)
+{
+    struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
+
+    /* Get the vitals from the interface. */
+    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+    if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
+       if (!maybe || (errno != ENODEV))
+           fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
+                   ifname, strerror(errno));
+       return 1;
+    }
+
+    if (override_phy >= 0) {
+       printf("using the specified MII index %d.\n", override_phy);
+       mii->phy_id = override_phy;
+    }
+
+    if (opt_reset) {
+       printf("resetting the transceiver...\n");
+       mdio_write(skfd, MII_BMCR, MII_BMCR_RESET);
+    }
+    if (nway_advertise) {
+       mdio_write(skfd, MII_ANAR, nway_advertise | 1);
+       opt_restart = 1;
+    }
+    if (opt_restart) {
+       printf("restarting autonegotiation...\n");
+       mdio_write(skfd, MII_BMCR, 0x0000);
+       mdio_write(skfd, MII_BMCR, MII_BMCR_AN_ENA|MII_BMCR_RESTART);
+    }
+    if (fixed_speed) {
+       int bmcr = 0;
+       if (fixed_speed & (MII_AN_100BASETX_FD|MII_AN_100BASETX_HD))
+           bmcr |= MII_BMCR_100MBIT;
+       if (fixed_speed & (MII_AN_100BASETX_FD|MII_AN_10BASET_FD))
+           bmcr |= MII_BMCR_DUPLEX;
+       mdio_write(skfd, MII_BMCR, bmcr);
+    }
+
+    if (!opt_restart && !opt_reset && !fixed_speed && !nway_advertise)
+       show_basic_mii(skfd, mii->phy_id);
+
+    return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+static void watch_one_xcvr(int skfd, char *ifname, int index)
+{
+    struct mii_data *mii = (struct mii_data *)&ifr.ifr_data;
+    static int status[MAX_ETH] = { 0, /* ... */ };
+    int now;
+
+    /* Get the vitals from the interface. */
+    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+    if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
+       if (errno != ENODEV)
+           fprintf(stderr, "SIOCGMIIPHY on '%s' failed: %s\n",
+                   ifname, strerror(errno));
+       return;
+    }
+    now = (mdio_read(skfd, MII_BMCR) |
+          (mdio_read(skfd, MII_BMSR) << 16));
+    if (status[index] && (status[index] != now))
+       show_basic_mii(skfd, mii->phy_id);
+    status[index] = now;
+}
+
+/*--------------------------------------------------------------------*/
+
+const char *usage =
+"usage: %s [-VvRrwl] [-A media,... | -F media] [interface ...]
+       -V, --version               display version information
+       -v, --verbose               more verbose output
+       -R, --reset                 reset MII to poweron state
+       -r, --restart               restart autonegotiation
+       -w, --watch                 monitor for link status changes
+       -l, --log                   with -w, write events to syslog
+       -A, --advertise=media,...   advertise only specified media
+       -F, --force=media           force specified media technology
+media: 100baseT4, 100baseTx-FD, 100baseTx-HD, 10baseT-FD, 10baseT-HD,
+       (to advertise both HD and FD) 100baseTx, 10baseT\n";
+
+int main(int argc, char **argv)
+{
+    int i, c, ret, errflag = 0;
+    char s[6];
+    
+    while ((c = getopt_long(argc, argv, "A:F:p:lrRvVw?", longopts, 0)) != EOF)
+       switch (c) {
+       case 'A': nway_advertise = parse_media(optarg); break;
+       case 'F': fixed_speed = parse_media(optarg); break;
+       case 'p': override_phy = atoi(optarg); break;
+       case 'r': opt_restart++;        break;
+       case 'R': opt_reset++;          break;
+       case 'v': verbose++;            break;
+       case 'V': opt_version++;        break;
+       case 'w': opt_watch++;          break;
+       case 'l': opt_log++;            break;
+       case '?': errflag++;
+       }
+    /* Check for a few inappropriate option combinations */
+    if (opt_watch) verbose = 0;
+    if (errflag || (fixed_speed & (fixed_speed-1)) ||
+       (fixed_speed && (opt_restart || nway_advertise))) {
+       fprintf(stderr, usage, argv[0]);
+       return 2;
+    }
+
+    if (opt_version)
+       printf(version);
+
+    /* Open a basic socket. */
+    if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
+       perror("socket");
+       exit(-1);
+    }
+
+    /* No remaining args means show all interfaces. */
+    if (optind == argc) {
+       ret = 1;
+       for (i = 0; i < MAX_ETH; i++) {
+           sprintf(s, "eth%d", i);
+           ret &= do_one_xcvr(skfd, s, 1);
+       }
+       if (ret)
+           fprintf(stderr, "no MII interfaces found\n");
+    } else {
+       ret = 0;
+       for (i = optind; i < argc; i++) {
+           ret |= do_one_xcvr(skfd, argv[i], 0);
+       }
+    }
+
+    if (opt_watch && (ret == 0)) {
+       while (1) {
+           sleep(1);
+           if (optind == argc) {
+               for (i = 0; i < MAX_ETH; i++) {
+                   sprintf(s, "eth%d", i);
+                   watch_one_xcvr(skfd, s, i);
+               }
+           } else {
+               for (i = optind; i < argc; i++)
+                   watch_one_xcvr(skfd, argv[i], i-optind);
+           }
+       }
+    }
+
+    close(skfd);
+    return ret;
+}