Blame SOURCES/mii-diag.c

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