/* $Id: hty_check.c,v 1.2 91/09/23 12:56:32 don Exp $ */
/* ex:set ts=4 sw=4: */
/****************************************************************/
/* hty_check													*/
/****************************************************************/
/*                 Confidential Information                     */
/*      Limited Distribution to Authorized Persons Only         */
/*     Created 1991 and Protected as an Unpublished Work        */
/*          Under the U.S. Copyright Act of 1976.               */
/*         Copyright (c) 1991, Systech Corporation              */
/*                  All Rights Reserved                         */
/*																*/
/*	THIS SOFTWARE IS LICENSED BY AND IS THE	CONFIDENTIAL AND 	*/
/*	PROPRIETARY PROPERTY OF SYSTECH COMPUTER CORPORATION AND 	*/
/*	MAY NOT, WITHOUT THE PRIOR WRITTEN CONSENT OF SYSTECH 		*/
/*	COMPUTER CORPORATION, BE REPRODUCED, USED OR DISCLOSED 		*/
/*	EXCEPT IN ACCORDANCE WITH A SYSTECH COMPUTER CORPORATION 	*/
/*	SOFTWARE LICENSE AGREEMENT.									*/
/****************************************************************/
/* This is a utility to check out an HPS port.  This is 		*/
/* primarily designed to check out a new installation.			*/
/* It may be used to debug a faulty tty port.					*/
/* 																*/
/* The basic function of the program is to open the port and 	*/
/* output a test string to that port.  The port should be 		*/
/* specified as a standard Unix device name such as "/dev/hty0".*/
/* 																*/
/* The program will attempt to open the port with full modem 	*/
/* signals.   If the open does not return within 10 seconds (or	*/
/* n if specified), it will try again in "clocal" mode.  If that*/
/* fails, it will attempt to determine the cause of the problem.*/
/* 																*/
/* Optional parameters											*/
/* 	-b baud		specify the baud rate for the port.  Default 	*/
/* 					is 9600.									*/
/* 	-n seconds	specify the number of seconds to wait for 		*/
/* 					the open before trying again in clocal 		*/
/* 					mode.  Default is 10.						*/
/* 																*/
/* Errors that this program will attempt to identify:			*/
/* 	System problems												*/
/* 		HPS raw or hty driver not installed						*/
/* 		host adapter not installed								*/
/* 		host adapter not downloaded								*/
/* 	Configuration problems										*/
/* 		cluster offline											*/
/* 		port not configured in HPS								*/
/* 		port not an HPS port									*/
/* 	Port problems												*/
/* 		terminal or device not present or missing modem signals	*/
/* 		wrong baud rate											*/
/*																*/
/****************************************************************/


/*
 *	INCLUDES
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <termio.h>
#include <signal.h>
#include "hps.std.h"
#include "hps.iocb.h"
#include "hps.ioctl.h"

/*
 *	IMPORTS
 */
extern int errno;
extern void exit();
extern char *optarg;
extern int optind, opterr;

/*
 *	LOCALS
 */
void dummy_alarm();
void open_error();

main(argc, argv)
int argc;
char **argv;
{
	struct stat buf;
	char errstr[80];
	char *aptr;
	int fd;
	int type;
	int c;
	unsigned int nseconds;
	int baud;
	int baudflag;
	int savebaud;
	int errflg;
	struct termio termio;

	nseconds = 5;
	baud = 9600;
	baudflag = B9600;
	errflg = 0;

	while ((c = getopt(argc, argv, "b:n:")) != -1)
	{
		switch (c) 
		{
		case 'n':	/* n = seconds to wait */
			nseconds = strtol(optarg, &aptr, 0);
			if (nseconds <= 0)
			{
				fprintf(stderr, "%s: number of seconds must be > 0\n", argv[0]);
				errflg++;
			}
			break;
		case 'b':	/* b = baud rate */
			baud = strtol(optarg, &aptr, 0);
			/* Validate baud rate */
			switch(baud)
			{
			case 75:	baudflag = B75; break;
			case 110:	baudflag = B110; break;
			case 134:	baudflag = B134; break;
			case 150:	baudflag = B150; break;
			case 300:	baudflag = B300; break;
			case 600:	baudflag = B600; break;
			case 1200:	baudflag = B1200; break;
			case 1800:	baudflag = B1800; break;
			case 2400:	baudflag = B2400; break;
			case 4800:	baudflag = B4800; break;
			case 9600:	baudflag = B9600; break;
			case 19200:	baudflag = EXTA; break;
			case 38400:	baudflag = EXTB; break;
			default:
				fprintf(stderr, "%s: Baud rate must be 75, 110, 134, 150, 300, 600,\n", argv[0]);
				fprintf(stderr, "    1200, 1800, 2400, 4800, 9600, 19200, or 38400\n");
				errflg++;
				break;
			}
			break;
		case '?':
			errflg++;
			break;
		}
	}

	if (optind >= argc)
		errflg++;

	if (errflg) 
	{
		fprintf(stderr, "usage: %s [-n seconds] [-b baud] htydev\n", argv[0]);
		exit(1);
	}

	printf("Checking %s\n", argv[optind]);
	if (stat(argv[optind], &buf) != 0)
	{
		sprintf(errstr, "     *ERROR* Unable to find file %s", argv[optind]);
		perror(errstr);
		exit(1);
	}

	if ((buf.st_mode & S_IFMT) == S_IFCHR)
	{
		printf("Character special device (major=%d/minor=%d)\n", 
			major(buf.st_rdev), minor(buf.st_rdev));
	}
	else
	{
		fprintf(stderr, "%s is not a device file.\n", argv[optind]);
		fprintf(stderr, "Device name argument example: /dev/hty00\n");
		exit(1);
	}

	signal(SIGALRM,dummy_alarm);
	alarm(nseconds);	/* Give the open some time to try to work */
	if ((fd = open(argv[optind], O_RDWR)) <= 0)
	{
		if (errno == EINTR)
		{
			printf("  *NOTE* Could not open port in %d seconds.\n", nseconds);
			printf("  *NOTE* Maybe missing terminal or modem signals.\n");
			printf("  *NOTE* Attempting open without modem signals (O_NDELAY).\n");
			alarm(nseconds);
			if ((fd = open(argv[optind], O_RDWR|O_NDELAY)) < 0)
			{
				open_error(argv[optind]);
				exit(1);
			}
			else 
			{
				printf("Successful open of port with O_NDELAY.\n");
				baudflag |= CLOCAL;
			}
		}
		else
		{
			open_error(argv[optind]);
			exit(1);
		}
	}
	else 
	{
		printf("Successful open of port.\n");
	}
	alarm(0);	/* Cancel any leftover timer */

	/* Try a private IOCTL to see what kind of port it is */
	if (ioctl(fd, TCGETTYPE, &type) < 0)
	{
		fprintf(stderr, "     *ERROR* Port is not an HPS port: %s\n", argv[optind]);
		exit(1);
	}

	printf("Port appears to be an HPS port ");
	if (type)
		printf("configured to monitor the DCD signal\n");
	else
		printf("configured to ignore the DCD signal\n");

	printf("Sending test data to port at %d baud\n", baud);

	if (ioctl(fd, TCGETA, &termio) < 0)
	{
		sprintf(errstr, "     *ERROR* Getting current baud rate on %s failed", argv[optind]);
		perror(errstr);
		exit(1);
	}

	savebaud = termio.c_cflag;
	termio.c_cflag &= ~CBAUD;
	termio.c_cflag |= baudflag;
	if (ioctl(fd, TCSETA, &termio) < 0)
	{
		sprintf(errstr, "     *ERROR* Setting baud rate on %s failed", argv[optind]);
		perror(errstr);
		exit(1);
	}

	alarm(nseconds);	/* Give the writes some time to try to work */

	if (write(fd, "abcdefghijklmnopqrstuvwxyz\n\r", 28) != 28)
	{
		sprintf(errstr, "     *ERROR* Unable to write (1) to port %s", argv[optind]);
		perror(errstr);
		exit(1);
	}
	if (write(fd, "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n\r", 28) != 28)
	{
		sprintf(errstr, "     *ERROR* Unable to write (2) to port %s", argv[optind]);
		perror(errstr);
		exit(1);
	}
	if (write(fd, "0123456789\n\r", 12) != 12)
	{
		sprintf(errstr, "     *ERROR* Unable to write (3) to port %s", argv[optind]);
		perror(errstr);
		exit(1);
	}

	alarm(0);	/* Cancel any leftover timer */

	termio.c_cflag = savebaud;
	if (ioctl(fd, TCSETAW, &termio) < 0)
	{
		sprintf(errstr, "     *ERROR* Restoring baud rate on %s failed", argv[optind]);
		perror(errstr);
		exit(1);
	}

	if (close(fd) < 0)
	{
		sprintf(errstr, "     *ERROR* Unable to close port %s", argv[optind]);
		perror(errstr);
		exit(1);
	}

	printf("Apparent success.  Check terminal for output which should look like:\n");
	printf("abcdefghijklmnopqrstuvwxyz\n");
	printf("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
	printf("0123456789\n");
	return(0);
}


/*
 *	FUNCTION dummy_alarm - a do nothing routine for alarm to call
 *
 *	DESCRIPTION
 *		Only resets the signal for next time.
 *
 *	PARAMETERS
 *		none
 *
 *	RETURN VALUES
 *		none
 */
void dummy_alarm()
{
	signal(SIGALRM,dummy_alarm);
}


/*
 *	FUNCTION open_error - interpret the error from open
 *
 *	DESCRIPTION
 *		This routine attempts to figure out WHY the open
 *		failed.  It looks at errno to see if it is something
 *		returned by the driver.  If so, gives possible causes.
 *		If not, must be a problem with the OS or user.
 *		Prints out best description of problem.
 *
 *	PARAMETERS
 *		devname	- device name that failed on open
 *
 *	RETURN VALUES
 *		none
 */
void open_error(devname)
char *devname;
{
	char errstr[80];
	int save_error;

	save_error = errno;
	sprintf(errstr, "     *ERROR* Unable to open port %s", devname);
	perror(errstr);
	switch (save_error)
	{
	case ENXIO:
		fprintf(stderr, "Error (ENXIO) may be caused by one of the following:\n");
		fprintf(stderr, 
			"   - requested channel is not in configured range (hty_config)\n");
		fprintf(stderr, "   - hty driver not configured (hty_load utility)\n");
		fprintf(stderr, "   - drivers have not been installed\n");
		break;
	case ENODEV:
		fprintf(stderr, 
			"Error (ENODEV) may be caused by one of the following:\n");
		fprintf(stderr, 
			"   - HPS board hasn't been downloaded (hps_load utility)\n");
		fprintf(stderr, "   - drivers have not been installed\n");
		break;
	case EINTR:
		/* NOTE: theoretically this cannot happen.  We check for EINTR
			returned from OPEN */
		fprintf(stderr, "Error (EINTR) caused by:\n");
		fprintf(stderr, 
			"   - open was interrupted while waiting for modem signals\n");
		break;
	case EIO:
		fprintf(stderr, "Error (EIO) caused by:\n");
		fprintf(stderr, 
			"   - commands to board failed.  Possibly the HPS\n");
		fprintf(stderr, 
			"     board hasn't been downloaded (hps_load utility)\n");
		break;
	default:
		fprintf(stderr, "This error returned from the OS, not the driver\n");
		fprintf(stderr, "Possible causes:\n");
		fprintf(stderr, "   - permissions not correct on device entry\n");
		fprintf(stderr, "   - device entry was not created correctly\n");
		fprintf(stderr, "   - device entry is not an hty port\n");
		fprintf(stderr, "   - drivers have not been installed\n");
		break;
	}
}
/*
 * MODIFICATION HISTORY:
 *
 * $Log:	hty_check.c,v $
 * Revision 1.1.2.1  92/06/24  13:53:41  marker
 * Branch RISCOS_5_01_2_FIXES off of trunk.
 * 
 * Revision 1.2  91/09/23  12:56:32  don
 * Modified to clean up as per code review.  Lots of little things.
 * Summary follows:
 * use hps.std.h instead of hps.types.h
 * Declare all used routines in declaration section.
 * Use strtol instead of atoi.
 * Specific error messages for nseconds or baud errors.
 * exit(1) instead of exit(2) on errors.
 * Reworded messages re: DCE port configuration
 * Set and restore baud rate unconditionally.
 * return(0) at end.
 * Reworded error messages in open_error().
 * INTERNAL
 * 
 * Revision 1.1  91/09/13  12:59:30  don
 * Initial revision
 * 
 * 
 */
