1c4448
/* Mode: C;
1c4448
 * mii-diag.c: Examine and set the MII registers of a network interfaces.
1c4448
1c4448
	Usage:	mii-diag [-vw] interface.
1c4448
1c4448
	This program reads and writes the Media Independent Interface (MII)
1c4448
	management registers on network transceivers.  The registers control
1c4448
	and report network link settings and errors.  Examples are link speed,
1c4448
	duplex, capabilities advertised to the link partner, status LED
1c4448
	indications and link error counters.
1c4448
1c4448
	Notes:
1c4448
	The compile-command is at the end of this source file.
1c4448
	This program works with drivers that implement MII ioctl() calls.
1c4448
1c4448
	Written/copyright 1997-2003 by Donald Becker <becker@scyld.com>
1c4448
1c4448
	This program is free software; you can redistribute it
1c4448
	and/or modify it under the terms of the GNU General Public
1c4448
	License as published by the Free Software Foundation.
1c4448
1c4448
	The author may be reached as becker@scyld.com, or C/O
1c4448
	 Scyld Computing Corporation
1c4448
	 914 Bay Ridge Road, Suite 220
1c4448
	 Annapolis MD 21403
1c4448
1c4448
	References
1c4448
	http://scyld.com/expert/mii-status.html
1c4448
	http://scyld.com/expert/NWay.html
1c4448
	http://www.national.com/pf/DP/DP83840.html
1c4448
*/
1c4448
1c4448
static char version[] =
1c4448
"mii-diag.c:v2.11 3/21/2005 Donald Becker (becker@scyld.com)\n"
1c4448
" http://www.scyld.com/diag/index.html\n";
1c4448
1c4448
static const char usage_msg[] =
1c4448
"Usage: %s [--help] [-aDfrRvVw] [-AF <speed+duplex>] [--watch] <interface>\n";
1c4448
static const char long_usage_msg[] =
1c4448
"Usage: %s [-aDfrRvVw] [-AF <speed+duplex>] [--watch] <interface>\n\
1c4448
\n\
1c4448
  This program configures and monitors the transceiver management registers\n\
1c4448
  for network interfaces.  It uses the Media Independent Interface (MII)\n\
1c4448
  standard with additional Linux-specific controls to communicate with the\n\
1c4448
  underlying device driver.  The MII registers control and report network\n\
1c4448
  link settings and errors.  Examples are link speed, duplex, capabilities\n\
1c4448
  advertised to the link partner, status LED indications and link error\n\
1c4448
  counters.\n\
1c4448
\n\
1c4448
   The common usage is\n\
1c4448
      mii-diag eth0\n\
1c4448
\n\
1c4448
 Frequently used options are\n\
1c4448
   -A  --advertise <speed|setting>\n\
1c4448
   -F  --fixed-speed <speed>\n\
1c4448
	Speed is one of: 100baseT4, 100baseTx, 100baseTx-FD, 100baseTx-HD,\n\
1c4448
	                 10baseT, 10baseT-FD, 10baseT-HD\n\
1c4448
   -s  --status     Return exit status 2 if there is no link beat.\n\
1c4448
\n\
1c4448
 Less frequently used options are\n\
1c4448
   -a  --all-interfaces  Show the status all interfaces\n\
1c4448
              (Not recommended with options that change settings.)\n\
1c4448
   -D  --debug\n\
1c4448
   -g  --read-parameters 	Get driver-specific parameters.\n\
1c4448
   -G  --set-parameters PARMS	Set driver-specific parameters.\n\
1c4448
       Parameters are comma separated, missing parameters retain\n\
1c4448
       their previous values.\n\
1c4448
   -M  --msg-level LEVEL 	Set the driver message bit map.\n\
1c4448
   -p  --phy ADDR		Set the PHY (MII address) to report.\n\
1c4448
   -r  --restart	Restart the link autonegotiation.\n\
1c4448
   -R  --reset		Reset the transceiver.\n\
1c4448
   -v  --verbose	Report each action taken.\n\
1c4448
   -V  --version	Emit version information.\n\
1c4448
   -w  --watch		Continuously monitor the transceiver and report changes.\n\
1c4448
\n\
1c4448
   This command returns success (zero) if the interface information can be\n\
1c4448
   read.  If the --status option is passed, a zero return means that the\n\
1c4448
   interface has link beat.\n\
1c4448
";
1c4448
1c4448
#include <unistd.h>
1c4448
#include <stdlib.h>
1c4448
#include <stdio.h>
1c4448
#include <ctype.h>
1c4448
#include <string.h>
1c4448
#include <errno.h>
1c4448
#include <fcntl.h>
1c4448
#include <getopt.h>
1c4448
#include <sys/types.h>
1c4448
#include <sys/socket.h>
1c4448
#include <sys/ioctl.h>
1c4448
#include <net/if.h>
1c4448
#ifdef use_linux_libc5
1c4448
#include <linux/if_arp.h>
1c4448
#include <linux/if_ether.h>
1c4448
#endif
1c4448
1c4448
typedef u_int32_t u32;
1c4448
typedef u_int16_t u16;
1c4448
typedef u_int8_t u8;
1c4448
1c4448
#if defined(SIOCGPARAMS)  && SIOCGPARAMS != SIOCDEVPRIVATE+3
1c4448
#error Changed definition for SIOCGPARAMS
1c4448
#else
1c4448
#define SIOCGPARAMS (SIOCDEVPRIVATE+3) 		/* Read operational parameters. */
1c4448
#define SIOCSPARAMS (SIOCDEVPRIVATE+4) 		/* Set operational parameters. */
1c4448
#endif
1c4448
1c4448
const char shortopts[] = "aA:C:DfF:gG:hmM:p:rRsvVw?";
1c4448
struct option longopts[] = {
1c4448
 /* { name  has_arg  *flag  val } */
1c4448
    {"all-interfaces", 0, 0, 'a'},	/* Show all interfaces. */
1c4448
	{"advertise",	1, 0, 'A'},		/* Change the capabilities advertised. */
1c4448
	{"BMCR",		1, 0, 'C'},		/* Set the control register. */
1c4448
    {"debug",       0, 0, 'D'},		/* Increase the debug level. */
1c4448
    {"force",       0, 0, 'f'},		/* Force the operation. */
1c4448
    {"fixed-speed", 1, 0, 'F'},		/* Fixed speed name. */
1c4448
    {"read-parameters", 0, 0, 'g'}, /* Show general settings values. */
1c4448
    {"set-parameters",  1, 0, 'G'},	/* Write general settings values. */
1c4448
    {"help", 		0, 0, 'h'},		/* Print a long usage message. */
1c4448
    {"monitor",		0, 0, 'm'},		/* Monitor status register. */
1c4448
    {"msg-level",	1, 0, 'M'},		/* Set the driver message level. */
1c4448
    {"phy",			1, 0, 'p'},		/* Set the PHY (MII address) to report. */
1c4448
    {"restart",		0, 0, 'r'},		/* Restart the link negotiation */
1c4448
    {"reset",		0, 0, 'R'},		/* Reset the transceiver. */
1c4448
    {"status",		0, 0, 's'},		/* Non-zero exit status w/ no link beat. */
1c4448
    {"verbose", 	0, 0, 'v'},		/* Report each action taken.  */
1c4448
    {"version", 	0, 0, 'V'},		/* Emit version information.  */
1c4448
    {"watch", 		0, 0, 'w'},		/* Constantly monitor the port.  */
1c4448
    {"error", 		0, 0, '?'},		/* Return the error message. */
1c4448
    { 0, 0, 0, 0 }
1c4448
};
1c4448
1c4448
/* Usually in libmii.c, but trivial substitions are below. */
1c4448
extern int  show_mii_details(long ioaddr, int phy_id);
1c4448
extern void monitor_mii(long ioaddr, int phy_id);
1c4448
int  show_mii_details(long ioaddr, int phy_id) __attribute__((weak));
1c4448
void monitor_mii(long ioaddr, int phy_id) __attribute__((weak));
1c4448
1c4448
1c4448
/* Command-line flags. */
1c4448
unsigned int opt_a = 0,					/* Show-all-interfaces flag. */
1c4448
	opt_f = 0,					/* Force the operation. */
1c4448
	opt_g = 0,
1c4448
	opt_G = 0,
1c4448
	verbose = 0,				/* Verbose flag. */
1c4448
	debug = 0,
1c4448
	opt_version = 0,
1c4448
	opt_restart = 0,
1c4448
	opt_reset = 0,
1c4448
	opt_status = 0,
1c4448
	opt_watch = 0;
1c4448
static int msg_level = -1;
1c4448
static int set_BMCR = -1;
1c4448
static int nway_advertise = 0;
1c4448
static int fixed_speed = -1;
1c4448
static int override_phy = -1;
1c4448
char *opt_G_string = NULL;
1c4448
1c4448
/* Internal values. */
1c4448
int new_ioctl_nums;
1c4448
int skfd = -1;					/* AF_INET socket for ioctl() calls.	*/
1c4448
struct ifreq ifr;
1c4448
1c4448
int do_one_xcvr(int skfd);
1c4448
int show_basic_mii(long ioaddr, int phy_id);
1c4448
int mdio_read(int skfd, int phy_id, int location);
1c4448
void mdio_write(int skfd, int phy_id, int location, int value);
1c4448
static int parse_advertise(const char *capabilities);
1c4448
static void monitor_status(long ioaddr, int phy_id);
1c4448
1c4448
1c4448
int
1c4448
main(int argc, char **argv)
1c4448
{
1c4448
	int c, errflag = 0;
1c4448
	char **spp, *ifname;
1c4448
    char *progname = rindex(argv[0], '/') ? rindex(argv[0], '/')+1 : argv[0];
1c4448
1c4448
	while ((c = getopt_long(argc, argv, shortopts, longopts, 0)) != EOF)
1c4448
		switch (c) {
1c4448
		case 'a': opt_a++; break;
1c4448
		case 'A': nway_advertise |= parse_advertise(optarg);
1c4448
			if (nway_advertise == -1) errflag++;
1c4448
			break;
1c4448
		case 'C': set_BMCR = strtoul(optarg, NULL, 16); break;
1c4448
		case 'D': debug++;			break;
1c4448
		case 'f': opt_f++; break;
1c4448
		case 'F': fixed_speed = parse_advertise(optarg);
1c4448
			if (fixed_speed == -1) errflag++;
1c4448
			break;
1c4448
		case 'g': opt_g++; break;
1c4448
		case 'G': opt_G++; opt_G_string = strdup(optarg); break;
1c4448
		case 'm': opt_watch++; opt_status++; break;
1c4448
		case 'M': msg_level = strtoul(optarg, NULL, 0); break;
1c4448
		case 'h': fprintf(stderr, long_usage_msg, progname); return 0;
1c4448
		case 'p': override_phy = atoi(optarg); break;
1c4448
		case 'r': opt_restart++;	break;
1c4448
		case 'R': opt_reset++;		break;
1c4448
		case 's': opt_status++;		break;
1c4448
		case 'v': verbose++;		break;
1c4448
		case 'V': opt_version++;	break;
1c4448
		case 'w': opt_watch++;		break;
1c4448
		case '?': errflag++;		break;
1c4448
		}
1c4448
	if (errflag) {
1c4448
		fprintf(stderr, usage_msg, progname);
1c4448
		return 2;
1c4448
	}
1c4448
1c4448
	if (verbose || opt_version)
1c4448
		printf("%s", version);
1c4448
1c4448
	/* Open a basic socket. */
1c4448
	if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
1c4448
		perror("socket");
1c4448
		return 1;
1c4448
	}
1c4448
1c4448
	if (debug)
1c4448
		fprintf(stderr, "DEBUG: argc=%d, optind=%d and argv[optind] is %s.\n",
1c4448
				argc, optind, argv[optind]);
1c4448
1c4448
	/* No remaining args means interface wasn't specified. */
1c4448
	if (optind == argc) {
1c4448
		fprintf(stderr, "No interface specified.\n");
1c4448
		fprintf(stderr, usage_msg, progname);
1c4448
		(void) close(skfd);
1c4448
		return 2;
1c4448
	} else {
1c4448
		/* Copy the interface name. */
1c4448
		spp = argv + optind;
1c4448
		ifname = *spp++;
1c4448
	}
1c4448
1c4448
	if (ifname == NULL) {
1c4448
		fprintf(stderr, "No ifname.\n");
1c4448
		(void) close(skfd);
1c4448
		return -1;
1c4448
	}
1c4448
1c4448
	/* Verify that the interface supports the ioctl(), and if
1c4448
	   it is using the new or old SIOCGMIIPHY value (grrr...).
1c4448
	 */
1c4448
	{
1c4448
		u16 *data = (u16 *)(&ifr.ifr_data);
1c4448
1c4448
		strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
1c4448
		ifr.ifr_name[IFNAMSIZ-1] = '\0';
1c4448
		data[0] = 0;
1c4448
1c4448
		if (ioctl(skfd, 0x8947, &ifr) >= 0) {
1c4448
			new_ioctl_nums = 1;
1c4448
		} else if (ioctl(skfd, SIOCDEVPRIVATE, &ifr) >= 0) {
1c4448
			new_ioctl_nums = 0;
1c4448
		} else {
1c4448
			fprintf(stderr, "SIOCGMIIPHY on %s failed: %s\n", ifname,
1c4448
					strerror(errno));
1c4448
			(void) close(skfd);
1c4448
			return 1;
1c4448
		}
1c4448
		if (verbose)
1c4448
			printf("  Using the %s SIOCGMIIPHY value on PHY %d "
1c4448
				   "(BMCR 0x%4.4x).\n",
1c4448
				   new_ioctl_nums ? "new" : "old", data[0], data[3]);
1c4448
	}
1c4448
1c4448
	do_one_xcvr(skfd);
1c4448
1c4448
	(void) close(skfd);
1c4448
	return 0;
1c4448
}
1c4448
1c4448
int do_one_xcvr(int skfd)
1c4448
{
1c4448
	u16 *data = (u16 *)(&ifr.ifr_data);
1c4448
	u32 *data32 = (u32 *)(&ifr.ifr_data);
1c4448
	unsigned phy_id = data[0];
1c4448
1c4448
	if (override_phy >= 0) {
1c4448
		printf("Using the specified MII PHY index %d.\n", override_phy);
1c4448
		phy_id = override_phy;
1c4448
	}
1c4448
1c4448
	if (opt_g || opt_G || msg_level >= 0) {
1c4448
		if (ioctl(skfd, SIOCGPARAMS, &ifr) < 0) {
1c4448
			fprintf(stderr, "SIOCGPARAMS on %s failed: %s\n", ifr.ifr_name,
1c4448
					strerror(errno));
1c4448
			return -1;
1c4448
		}
1c4448
	}
1c4448
	if (opt_g) {
1c4448
		int i;
1c4448
		printf("Driver general parameter settings:");
1c4448
		for (i = 0; i*sizeof(u32) < sizeof(ifr.ifr_ifru); i++) {
1c4448
			printf(" %d", data32[i]);
1c4448
		}
1c4448
		printf(".\n");
1c4448
	}
1c4448
	if (opt_G) {
1c4448
		/* Set up to four arbitrary driver parameters from the -G parameter.
1c4448
		   The format is comma separated integers, with a missing element
1c4448
		   retaining the previous value.
1c4448
		*/
1c4448
		char *str = opt_G_string;
1c4448
		int i;
1c4448
		for (i = 0; str && i < 4; i++) {
1c4448
			char *endstr;
1c4448
			u32 newval = strtol(str, &endstr, 0);
1c4448
			if (debug)
1c4448
				printf(" parse string '%s'  value %d end '%s'.\n",
1c4448
					   str, newval, endstr);
1c4448
			if (str == endstr) {
1c4448
				if (endstr[0] == ',') /* No parameter */
1c4448
					str = endstr+1;
1c4448
				else {
1c4448
					fprintf(stderr, "Invalid driver parameter '%s'.\n", str);
1c4448
					str = index(str, ',');
1c4448
				}
1c4448
			} else if (endstr[0] == ',') {
1c4448
				data32[i] = newval;
1c4448
				str = endstr + 1;
1c4448
			} else if (endstr[0] == 0) {
1c4448
				data32[i] = newval;
1c4448
				break;
1c4448
			}
1c4448
		}
1c4448
		printf("Setting new driver general parameters:");
1c4448
		for (i = 0; i*sizeof(u32) < sizeof(ifr.ifr_ifru); i++) {
1c4448
			printf(" %d", data32[i]);
1c4448
		}
1c4448
		printf(".\n");
1c4448
		if (ioctl(skfd, SIOCSPARAMS, &ifr) < 0) {
1c4448
			fprintf(stderr, "SIOCSPARAMS on %s failed: %s\n", ifr.ifr_name,
1c4448
					strerror(errno));
1c4448
			return -1;
1c4448
		}
1c4448
	}
1c4448
	if (msg_level >= 0) {
1c4448
		data32[0] = msg_level;
1c4448
		if (ioctl(skfd, SIOCSPARAMS, &ifr) < 0) {
1c4448
			fprintf(stderr, "SIOCSPARAMS on %s failed: %s\n", ifr.ifr_name,
1c4448
					strerror(errno));
1c4448
			return -1;
1c4448
		}
1c4448
	}
1c4448
1c4448
	if (opt_reset) {
1c4448
		printf("Resetting the transceiver...\n");
1c4448
		mdio_write(skfd, phy_id, 0, 0x8000);
1c4448
	}
1c4448
	/* Note: PHY addresses > 32 are pseudo-MII devices, usually built-in. */
1c4448
	if (phy_id < 64  &&  nway_advertise > 0) {
1c4448
		printf(" Setting the media capability advertisement register of "
1c4448
			   "PHY #%d to 0x%4.4x.\n", phy_id, nway_advertise | 1);
1c4448
		mdio_write(skfd, phy_id, 4, nway_advertise | 1);
1c4448
		mdio_write(skfd, phy_id, 0, 0x1000);
1c4448
	}
1c4448
1c4448
	if (opt_restart) {
1c4448
		printf("Restarting negotiation...\n");
1c4448
		mdio_write(skfd, phy_id, 0, 0x0000);
1c4448
		mdio_write(skfd, phy_id, 0, 0x1200);
1c4448
	}
1c4448
	/* To force 100baseTx-HD do  mdio_write(skfd, phy_id, 0, 0x2000); */
1c4448
	if (fixed_speed >= 0) {
1c4448
		int reg0_val = 0;
1c4448
		if (fixed_speed & 0x0180) 		/* 100mpbs */
1c4448
			reg0_val |=  0x2000;
1c4448
		if ((fixed_speed & 0x0140) &&		/* A full duplex type and */
1c4448
			! (fixed_speed & 0x0820)) 		/* no half duplex types. */
1c4448
			reg0_val |= 0x0100;
1c4448
		printf("Setting the speed to \"fixed\", Control register %4.4x.\n",
1c4448
			   reg0_val);
1c4448
		mdio_write(skfd, phy_id, 0, reg0_val);
1c4448
	}
1c4448
	if (set_BMCR >= 0) {
1c4448
		printf("Setting the Basic Mode Control Register to 0x%4.4x.\n",
1c4448
			   set_BMCR);
1c4448
		mdio_write(skfd, phy_id, 0, set_BMCR);
1c4448
	}
1c4448
1c4448
	if (opt_watch && opt_status)
1c4448
		monitor_status(skfd, phy_id);
1c4448
1c4448
	show_basic_mii(skfd, phy_id);
1c4448
#ifdef LIBMII
1c4448
	if (verbose)
1c4448
		show_mii_details(skfd, phy_id);
1c4448
#else
1c4448
	if (verbose || debug) {
1c4448
		int mii_reg, mii_val;
1c4448
		printf(" MII PHY #%d transceiver registers:", phy_id);
1c4448
		for (mii_reg = 0; mii_reg < 32; mii_reg++) {
1c4448
			mii_val = mdio_read(skfd, phy_id, mii_reg);
1c4448
			printf("%s %4.4x", (mii_reg % 8) == 0 ? "\n  " : "",
1c4448
				   mii_val);
1c4448
		}
1c4448
		printf("\n");
1c4448
	}
1c4448
#endif
1c4448
1c4448
	if (opt_watch)
1c4448
		monitor_mii(skfd, phy_id);
1c4448
	if (opt_status &&
1c4448
		(mdio_read(skfd, phy_id, 1) & 0x0004) == 0)
1c4448
		exit(2);
1c4448
	return 0;
1c4448
}
1c4448
1c4448
int mdio_read(int skfd, int phy_id, int location)
1c4448
{
1c4448
	u16 *data = (u16 *)(&ifr.ifr_data);
1c4448
1c4448
	data[0] = phy_id;
1c4448
	data[1] = location;
1c4448
1c4448
	if (ioctl(skfd, new_ioctl_nums ? 0x8948 : SIOCDEVPRIVATE+1, &ifr) < 0) {
1c4448
		fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,
1c4448
				strerror(errno));
1c4448
		return -1;
1c4448
	}
1c4448
	return data[3];
1c4448
}
1c4448
1c4448
void mdio_write(int skfd, int phy_id, int location, int value)
1c4448
{
1c4448
	u16 *data = (u16 *)(&ifr.ifr_data);
1c4448
1c4448
	data[0] = phy_id;
1c4448
	data[1] = location;
1c4448
	data[2] = value;
1c4448
1c4448
	if (ioctl(skfd, new_ioctl_nums ? 0x8949 : SIOCDEVPRIVATE+2, &ifr) < 0) {
1c4448
		fprintf(stderr, "SIOCSMIIREG on %s failed: %s\n", ifr.ifr_name,
1c4448
				strerror(errno));
1c4448
	}
1c4448
}
1c4448
1c4448
/* Parse the command line argument for advertised capabilities. */
1c4448
static int parse_advertise(const char *capabilities)
1c4448
{
1c4448
	const char *mtypes[] = {
1c4448
		"100baseT4", "100baseTx", "100baseTx-FD", "100baseTx-HD",
1c4448
		"10baseT", "10baseT-FD", "10baseT-HD", 0,
1c4448
	};
1c4448
	char *endptr;
1c4448
	int cap_map[] = { 0x0200, 0x0180, 0x0100, 0x0080, 0x0060, 0x0040, 0x0020,};
1c4448
	int i;
1c4448
	if ( ! capabilities) {
1c4448
		fprintf(stderr, "You passed -A 'NULL'.  You must provide a media"
1c4448
				" list to advertise!\n");
1c4448
		return -1;
1c4448
	}
1c4448
	if (debug)
1c4448
		fprintf(stderr, "Advertise string is '%s'.\n", capabilities);
1c4448
	for (i = 0; mtypes[i]; i++)
1c4448
		if (strcasecmp(mtypes[i], capabilities) == 0)
1c4448
			return cap_map[i];
1c4448
	if ((i = strtol(capabilities, &endptr, 16)) <= 0xffff  &&  endptr[0] == 0)
1c4448
		return i;
1c4448
	fprintf(stderr, "Invalid media advertisement value '%s'.\n"
1c4448
			"  Either pass a numeric value or one of the following names:\n",
1c4448
			capabilities);
1c4448
	for (i = 0; mtypes[i]; i++)
1c4448
		fprintf(stderr, "   %-14s %3.3x\n", mtypes[i], cap_map[i]);
1c4448
	return -1;
1c4448
}
1c4448
1c4448
/* Trivial versions if we don't link against libmii.c */
1c4448
static const char *media_names[] = {
1c4448
	"10baseT", "10baseT-FD", "100baseTx", "100baseTx-FD", "100baseT4",
1c4448
	"Flow-control", 0,
1c4448
};
1c4448
/* Various non-good bits in the command register. */
1c4448
static const char *bmcr_bits[] = {
1c4448
	"  Internal Collision-Test enabled!\n", "",		/* 0x0080,0x0100 */
1c4448
	"  Restarted auto-negotiation in progress!\n",
1c4448
	"  Transceiver isolated from the MII!\n",
1c4448
	"  Transceiver powered down!\n", "", "",
1c4448
	"  Transceiver in loopback mode!\n",
1c4448
	"  Transceiver currently being reset!\n",
1c4448
};
1c4448
1c4448
int show_basic_mii(long ioaddr, int phy_id)
1c4448
{
1c4448
	int mii_reg, i;
1c4448
	u16 mii_val[32];
1c4448
	u16 bmcr, bmsr, new_bmsr, nway_advert, lkpar;
1c4448
1c4448
	for (mii_reg = 0; mii_reg < 8; mii_reg++)
1c4448
		mii_val[mii_reg] = mdio_read(ioaddr, phy_id, mii_reg);
1c4448
	if ( ! verbose) {
1c4448
		printf("Basic registers of MII PHY #%d: ", phy_id);
1c4448
		for (mii_reg = 0; mii_reg < 8; mii_reg++)
1c4448
			printf(" %4.4x", mii_val[mii_reg]);
1c4448
		printf(".\n");
1c4448
	}
1c4448
1c4448
	if (mii_val[0] == 0xffff  ||  mii_val[1] == 0x0000) {
1c4448
		printf("  No MII transceiver present!.\n");
1c4448
		if (! opt_f) {
1c4448
			printf("  Use '--force' to view the information anyway.\n");
1c4448
			return -1;
1c4448
		}
1c4448
	}
1c4448
	/* Descriptive rename. */
1c4448
	bmcr = mii_val[0];
1c4448
	bmsr = mii_val[1];
1c4448
	nway_advert = mii_val[4];
1c4448
	lkpar = mii_val[5];
1c4448
1c4448
	if (lkpar & 0x4000) {
1c4448
		int negotiated = nway_advert & lkpar & 0x3e0;
1c4448
		int max_capability = 0;
1c4448
		/* Scan for the highest negotiated capability, highest priority
1c4448
		   (100baseTx-FDX) to lowest (10baseT-HDX). */
1c4448
		int media_priority[] = {8, 9, 7, 6, 5}; 	/* media_names[i-5] */
1c4448
		printf(" The autonegotiated capability is %4.4x.\n", negotiated);
1c4448
		for (i = 0; media_priority[i]; i++)
1c4448
			if (negotiated & (1 << media_priority[i])) {
1c4448
				max_capability = media_priority[i];
1c4448
				break;
1c4448
			}
1c4448
		if (max_capability)
1c4448
			printf("The autonegotiated media type is %s.\n",
1c4448
				   media_names[max_capability - 5]);
1c4448
		else
1c4448
			printf("No common media type was autonegotiated!\n"
1c4448
				   "This is extremely unusual and typically indicates a "
1c4448
				   "configuration error.\n" "Perhaps the advertised "
1c4448
				   "capability set was intentionally limited.\n");
1c4448
	}
1c4448
	printf(" Basic mode control register 0x%4.4x:", bmcr);
1c4448
	if (bmcr & 0x1000)
1c4448
		printf(" Auto-negotiation enabled.\n");
1c4448
	else
1c4448
		printf(" Auto-negotiation disabled, with\n"
1c4448
			   " Speed fixed at 10%s mbps, %s-duplex.\n",
1c4448
			   bmcr & 0x2000 ? "0" : "",
1c4448
			   bmcr & 0x0100 ? "full":"half");
1c4448
	for (i = 0; i < 9; i++)
1c4448
		if (bmcr & (0x0080<
1c4448
			printf("%s", bmcr_bits[i]);
1c4448
1c4448
	new_bmsr = mdio_read(ioaddr, phy_id, 1);
1c4448
	if ((bmsr & 0x0016) == 0x0004)
1c4448
		printf( " You have link beat, and everything is working OK.\n");
1c4448
	else
1c4448
		printf(" Basic mode status register 0x%4.4x ... %4.4x.\n"
1c4448
			   "   Link status: %sestablished.\n",
1c4448
			   bmsr, new_bmsr,
1c4448
			   bmsr & 0x0004 ? "" :
1c4448
			   (new_bmsr & 0x0004) ? "previously broken, but now re" : "not ");
1c4448
	if (verbose) {
1c4448
		printf("   This transceiver is capable of ");
1c4448
		if (bmsr & 0xF800) {
1c4448
			for (i = 15; i >= 11; i--)
1c4448
				if (bmsr & (1<
1c4448
					printf(" %s", media_names[i-11]);
1c4448
		} else
1c4448
			printf("<Warning! No media capabilities>");
1c4448
		printf(".\n");
1c4448
		printf("   %s to perform Auto-negotiation, negotiation %scomplete.\n",
1c4448
			   bmsr & 0x0008 ? "Able" : "Unable",
1c4448
			   bmsr & 0x0020 ? "" : "not ");
1c4448
	}
1c4448
1c4448
	if (bmsr & 0x0010)
1c4448
		printf(" Remote fault detected!\n");
1c4448
	if (bmsr & 0x0002)
1c4448
		printf("   *** Link Jabber! ***\n");
1c4448
1c4448
	if (lkpar & 0x4000) {
1c4448
		printf(" Your link partner advertised %4.4x:",
1c4448
			   lkpar);
1c4448
		for (i = 5; i >= 0; i--)
1c4448
			if (lkpar & (0x20<
1c4448
				printf(" %s", media_names[i]);
1c4448
		printf("%s.\n", lkpar & 0x0400 ? ", w/ 802.3X flow control" : "");
1c4448
	} else if (lkpar & 0x00A0)
1c4448
		printf(" Your link partner is generating %s link beat  (no"
1c4448
			   " autonegotiation).\n",
1c4448
			   lkpar & 0x0080 ? "100baseTx" : "10baseT");
1c4448
	else if ( ! (bmcr & 0x1000))
1c4448
		printf(" Link partner information is not exchanged when in"
1c4448
			   " fixed speed mode.\n");
1c4448
	else if ( ! (new_bmsr & 0x004))
1c4448
							;	/* If no partner, do not report status. */
1c4448
	else if (lkpar == 0x0001  ||  lkpar == 0x0000) {
1c4448
		printf(" Your link partner does not do autonegotiation, and this "
1c4448
			   "transceiver type\n  does not report the sensed link "
1c4448
			   "speed.\n");
1c4448
	} else
1c4448
		printf(" Your link partner is strange, status %4.4x.\n", lkpar);
1c4448
1c4448
	printf("   End of basic transceiver information.\n\n");
1c4448
	return 0;
1c4448
}
1c4448
1c4448
static void monitor_status(long ioaddr, int phy_id)
1c4448
{
1c4448
	unsigned int baseline_1 = 0x55555555; 	/* Always show initial status. */
1c4448
1c4448
	while (1) {
1c4448
		unsigned int new_1 = mdio_read(ioaddr, phy_id, 1);
1c4448
		if (new_1 != baseline_1) {
1c4448
			printf("%-12s 0x%4.4x 0x%4.4x\n",
1c4448
				   new_1 & 0x04 ? (new_1==0xffff ? "unknown" : "up") :
1c4448
				   new_1 & 0x20 ? "negotiating" : "down",
1c4448
				   new_1, mdio_read(ioaddr, phy_id, 5));
1c4448
			fflush(stdout);
1c4448
			baseline_1 = new_1;
1c4448
		}
1c4448
		sleep(1);
1c4448
	}
1c4448
}
1c4448
1c4448
int  show_mii_details(long ioaddr, int phy_id)
1c4448
{
1c4448
	int mii_reg, mii_val;
1c4448
	printf(" MII PHY #%d transceiver registers:", phy_id);
1c4448
	for (mii_reg = 0; mii_reg < 32; mii_reg++) {
1c4448
		mii_val = mdio_read(skfd, phy_id, mii_reg);
1c4448
		printf("%s %4.4x", (mii_reg % 8) == 0 ? "\n  " : "",
1c4448
			   mii_val);
1c4448
	}
1c4448
	printf("\nThis version of 'mii-diag' has not been linked with "
1c4448
			"the libmii.c library.\n"
1c4448
			"  That library provides extended transceiver status reports.\n");
1c4448
	return 0;
1c4448
}
1c4448
1c4448
void monitor_mii(long ioaddr, int phy_id)
1c4448
{
1c4448
	fprintf(stderr, "\nThis version of 'mii-diag' has not been linked with "
1c4448
			"the libmii.c library \n"
1c4448
			"  required for the media monitor option.\n");
1c4448
}
1c4448
1c4448
1c4448

1c4448
/*
1c4448
 * Local variables:
1c4448
 *  version-control: t
1c4448
 *  kept-new-versions: 5
1c4448
 *  c-indent-level: 4
1c4448
 *  c-basic-offset: 4
1c4448
 *  tab-width: 4
1c4448
 *  compile-command: "gcc -Wall -Wstrict-prototypes -O mii-diag.c -DLIBMII libmii.c -o mii-diag"
1c4448
 *  simple-compile-command: "gcc mii-diag.c -o mii-diag"
1c4448
 * End:
1c4448
 */