/****************************************************************************
 *									    *
 *			  COPYRIGHT (c) 1990 - 2004			    *
 *			   This Software Provided			    *
 *				     By					    *
 *			  Robin's Nest Software Inc.			    *
 *									    *
 * Permission to use, copy, modify, distribute and sell this software and   *
 * its documentation for any purpose and without fee is hereby granted,	    *
 * provided that the above copyright notice appear in all copies and that   *
 * both that copyright notice and this permission notice appear in the	    *
 * supporting documentation, and that the name of the author not be used    *
 * in advertising or publicity pertaining to distribution of the software   *
 * without specific, written prior permission.				    *
 *									    *
 * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 	    *
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN	    *
 * NO EVENT SHALL HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL   *
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR    *
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS  *
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF   *
 * THIS SOFTWARE.							    *
 *									    *
 ****************************************************************************/
/*
 * Module:	dttty.c
 * Author:	Robin T. Miller
 *
 * Description:
 *	Terminal line support functions for generic data test program.
 */
#include "dt.h"
#include <fcntl.h>
#if !defined(_QNX_SOURCE)
#  if !defined(sun)
#    include <sys/ioctl.h>
#  endif /* !defined(sun) */
#  include <sys/file.h>
#  include <sys/param.h>
#endif /* !defined(_QNX_SOURCE) */

/*
 * Define maximum values for VMIN & VTIME field values:
 */
#if defined(_QNX_SOURCE)
#  define VMIN_MAX	65535U
#  define VTIME_MAX	65535U
#else /* !defined(_QNX_SOURCE) */
#  define VMIN_MAX	255U
#  define VTIME_MAX	255U
#endif /* defined(_QNX_SOURCE) */

/*
 * Modification History:
 *
 * November 17th, 2003 by Robin Miller.
 *	Breakup output to stdout or stderr, rather than writing
 * all output to stderr.  If output file is stdout ('-') or a log
 * file is specified, then all output reverts to stderr.
 *
 * September 27th, 2003 by Robin Miller.
 *      Added support for AIX.
 *
 * February 23rd, 2002 by Robin Miller.
 *      Make porting changes for HP-UX IA64.
 *
 * December 30th, 2000 by Robin Miller.
 *	Make changes to build using MKS/NuTCracker product.
 *
 * May 8th, 2000 by Robin Miller.
 *	Honor the di_closing flag, to avoid a race condition with the
 * close function being called again while still closing, from the
 * terminate() routine called by the runtime= alarm, or signals.
 *
 * February 17th, 2000 by Robin Miller.
 *	Adding better support for multi-volume tape testing.
 *
 * July 29, 1999 by Robin Miller.
 *	Merge in changes made to compile on FreeBSD.
 *
 * May 27, 1999 by Robin Miller.
 *	Adding support for micro-second delays.
 *
 * December 19, 1995 by Robin Miller
 *      Conditionalize for Linux Operating System.
 *
 * December 7, 1995 by Robin Miller.
 *	Conditionally support speeds of 38400, 57600, 76800, & 115200.
 *
 * December 6, 1995 by Robin Miller.
 *	When setting the VMIN and VTIME, ensure the maximum values are
 *	not exceeded or blindly truncated to an incorrect value. These
 *	fields are currently declared as unsigned char's, so 255 is the
 *	maximum value possible.  [ I've been burnt by this too... ]
 *	Added additional baud rates supported by QNX Operating System.
 *
 * July 24, 1995 by Robin Miller.
 *	When doing modem testing, after waiting for carrier, disable
 *	non-blocking mode, to prevent EWOULDBLOCK on reads & writes.
 *	[ We open'ed w/O_NONBLOCK so we could read modem signals. ]
 *
 * October 29, 1993 by Robin Miller.
 *	Add test in tty_open() to ensure the terminal characteristics
 *	only get saved once when performing loopback to same terminal.
 *	Otherwise test attributes get restored, instead of original.
 *
 * September 7, 1993 by Robin Miller.
 *	Added tty specific test functions & test function dispatch table.
 *
 * September 24, 1992 by Robin Miller.
 *	If parity is being enabled, ensure input parity checking is also
 *	enabled, since these are independent of each other.  This change
 *	will allow parity and framing errors to come back as '\0' in the
 *	input stream, instead of silently being ignored.
 *
 * September 19, 1992 by Robin Miller.
 *	Added flushing of tty input & output queue in save_tty() to
 *	prevent unwarrented data comparison errors during testing. 
 *
 * September 18, 1992 by Robin Miller.
 *	Added save_tty() & restore_tty() routines to save & restore the
 *	terminal line discipline and characteristics.  This must be done
 *	since other programs may presume a certain tty setup.
 *
 * September 11, 1992 by Robin Miller.
 *	Change 3rd parmameter of TCBRK ioctl() to -1 instead of 1, to
 *	inhibit sending of actual break sequence and only drain output.
 *	This caused 'dt' to get intermittent data compare errors (ouch!).
 *
 * September 5, 1992 by Robin Miller.
 *	Initial port to QNX 4.1 Operating System.
 *
 * August 19, 1992 by Robin Miller.
 *	Added modem support functions and modem test setup.
 *
 * August 18, 1992 by Robin Miller.
 *	Added support for ULTRIX terminal driver setup (yuck!).
 *	Also ensure tty line discipline is setup properly.
 *
 * June 5, 1992 by Robin Miller.
 *	Added setting the terminal attributes for 2 stop bits for
 *	baud rates slower than 300 baud.
 *
 * May 25, 1992 by Robin Miller.
 *	Added POSIX compliant tty commands for OSF terminal driver.
 *	Ensure CLOCAL & HUPCL do not get cleared inadvertantly.
 *
 * October 16, 1989 by Robin Miller.
 *	Added setting the ISTRIP terminal characteristic when testing
 *	with even or odd parity.  This is needed for Sun's zs driver
 *	which passes the parity bit through if this isn't set, even
 *	though we have setup for only 7 data bits (SUN's bug).
 *
 * July 25, 1989 by Robin Miller.
 *	Add functions for setting up terminal chacteristics for testing
 *	terminal ports in loopback mode.
 *
 */

#if defined(__WIN32__) && !defined(__NUTC__)
extern int tcgetattr(int , struct termios *);
extern int tcsetattr(int , int , const struct termios *);
extern int tcsendbreak(int , int );
extern int tcdrain(int );
extern int tcflush(int , int );
extern int tcflow(int , int );
#endif /* defined(__WIN32__) && !defined(__NUTC__) */

/*
 * Table to Map Baudrate to tty Speed Codes.
 */
struct tty_baud_rate baud_rate_tbl[] = {
    {	0,	B0	},
    {	50,	B50	},
    {	75,	B75	},
    {	110,	B110	},
    {	134,	B134	},
    {	150,	B150	},
    {	200,	B200	},
    {	300,	B300	},
    {	600,	B600	},
    {	1200,	B1200	},
    {	1800,	B1800	},
    {	2400,	B2400	},
    {	4800,	B4800	},
    {	9600,	B9600	},
    {	19200,	B19200	},
#if defined(B38400)
    {	38400,	B38400	},
#endif /* defined(B38400) */
#if defined(B57600)
    {	57600,	B57600	},
#endif /* defined(B57600) */
#if defined(B76800)
    {	76800,	B76800	},
#endif /* defined(B76800) */
#if defined(B115200)
    {	115200,	B115200 },
#endif /* defined(B115200) */
    {	0,	0	}
};
int num_baud_rates = (sizeof(baud_rate_tbl) / sizeof(baud_rate_tbl[0])) - 1;

char *parity_str = "none";
char *flow_str = "xon_xoff";
char *speed_str = "9600";
speed_t baud_rate_code = B9600;		/* Default baud rate is 9600.	*/
unsigned parity_code = 0;		/* Default parity = none.	*/
unsigned data_bits_code = CS8;		/* Default data bits = 8.	*/

bool tty_saved;				/* tty characteristics saved.	*/
int saved_ldisc;			/* For saving line discipline.	*/
struct termios saved_tmodes;		/* For saving terminal modes.	*/

/*
 * Declare the terminal test functions.
 */
struct dtfuncs tty_funcs = {
    /*	tf_open,		tf_close,		tf_initialize,	  */
	tty_open,		tty_close,		initialize,
    /*  tf_start_test,		tf_end_test,				  */
	nofunc,			nofunc,
    /*	tf_read_file,		tf_read_data,		tf_cancel_reads,  */
	read_file,		read_data,		nofunc,
    /*	tf_write_file,		tf_write_data,		tf_cancel_writes, */
	write_file,		write_data,		nofunc,
    /*	tf_flush_data,		tf_verify_data,		tf_reopen_file,   */
	tty_flush_data,		verify_data,		tty_reopen,
    /*	tf_startup,		tf_cleanup,		tf_validate_opts  */
	nofunc,			nofunc,			validate_opts
};

/************************************************************************
 *									*
 * tty_open() - Open a terminal device for read/write access.		*
 *									*
 * Description:								*
 *	This function does the terminal device open processing.		*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		oflags = The device/file open flags.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCCESS / FAILURE.			*
 *									*
 ************************************************************************/
int
tty_open (struct dinfo *dip, int oflags)
{
    int status;

    if ((status = open_file (dip, oflags)) == FAILURE) {
	return (status);
    }

#if !defined(_QNX_SOURCE) && !defined(sun) && !defined(__MSDOS__) && !defined(__WIN32__) && !defined(HP_UX)
    if (!debug_flag && !loopback) {
	if ((status = ioctl (dip->di_fd, TIOCEXCL, 0)) == FAILURE) {
	    report_error ("TIOCEXCL", FALSE);
	    return (status);
	}
    }
#endif /* !defined(_QNX_SOURCE) */

    /*
     * Setup & optionally flush the input queue.
     */
    if (dip->di_mode == READ_MODE) {
	if ((status = save_tty (dip->di_fd)) < 0) return (status);
	if ((status = flush_tty (dip->di_fd)) < 0) return (status);
    } else if (!loopback) {
	/*
	 * If looping to ourselves, setup is already done above.
	 * [ Presumes input file opened before the output file. ]
	 */
	if ((status = save_tty (dip->di_fd)) < 0) return (status);
	if ((status = setup_tty (dip->di_fd, FALSE)) < 0) return (status);
    }
    verify_flag = FALSE;
    if ((dip->di_ftype == OUTPUT_FILE) && !loopback && (sdelay_count == 0)) {
	sdelay_count = 1;	/* Delay before write on tty. */
    }
    return (status);
}

/************************************************************************
 *									*
 * tty_close() - Close a terminal device file descriptor.		*
 *									*
 * Description:								*
 *	This function does the terminal device close processing.	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCCESS / FAILURE.			*
 *									*
 ************************************************************************/
int
tty_close (struct dinfo *dip)
{
    if (dip->di_closing || dip->di_fd == NoFd) {
	return (SUCCESS);		/* Closing or not open. */
    }
    if (tty_saved == TRUE) {
	(void) restore_tty (dip->di_fd);
    }
    return (close_file (dip));
}

/************************************************************************
 *									*
 * tty_reopen() - Reopen Terminal Devices.				*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		oflags = The device/file open flags.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCESS / FAILURE.			*
 *									*
 ************************************************************************/
/*ARGSUSED*/
int
tty_reopen (struct dinfo *dip, int oflags)
{
    int status = SUCCESS;

    /*
     * For terminal devices, we don't close the device since this
     * resets characteristics which must then be reset.
     */
    if (edelay_count) {			/* Optional end delay. 	*/
	mySleep (edelay_count);
    }

#ifdef notdef
    if ((status = reopen_file (dip, oflags)) == SUCCESS) {
	(void) setup_tty (&dip->di_fd, FALSE);
    }
#else
    end_of_file = FALSE;
#endif /* notdef */

    return (status);
}

/************************************************************************
 *									*
 * tty_flush_data() - Wait for tty output queue to empty (drain).	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCESS / FAILURE.			*
 *									*
 ************************************************************************/
int
tty_flush_data (struct dinfo *dip)
{
	return (drain_tty (dip->di_fd));
}

/************************************************************************
 *									*
 * drain_tty() - Wait for tty output data to drain.			*
 *									*
 * Description:								*
 *	This function was added to drain the terminal ports' output	*
 * buffer.  This is needed to properly synchronize with the process	*
 * reading the data.  If the writer exits before the reader reads the	*
 * data with software carrier enabled, the output data gets flushed	*
 * (discarded) by terminal driver.					*
 *									*
 * Inputs:	fd = The terminal file descriptor to drain.		*
 *									*
 * Return Value:							*
 *		Returns SUCCESS / FAILURE.				*
 *									*
 ************************************************************************/
int
drain_tty (int fd)
{
	int status;

#if !defined(_QNX_SOURCE) && !defined(__MSDOS__) && !defined(__WIN32__) && !defined(HP_UX)
	/*
	 * If debug is enabled, display the characters not flushed
	 * in the output queue yet.
	 */
	if (debug_flag) {
	    int outq_size;

	    if (ioctl (fd, TIOCOUTQ, &outq_size) < 0) {
		report_error ("TIOCOUTQ", TRUE);
	    } else {
		Printf ("Characters remaining in output queue = %d\n",
								outq_size);
	    }
	}
#endif /* !defined(_QNX_SOURCE) */
	/*
	 * Wait for the output queue to drain.
	 */
	if (debug_flag) {
	    Printf ("Waiting for output queue to drain...\n");
	}
#if defined(ultrix)
	if ((status = ioctl (fd, TCSBRK, -1)) < 0) {
	    if (debug_flag) report_error ("TCSBRK", TRUE);
#else /* !defined(ultrix) */
	if ((status = tcdrain (fd)) < 0) {
	    report_error ("tcdrain()", TRUE);
#endif /* defined(ultrix) */
	    /*
	     * Since some drivers don't support this ioctl, we must
	     * get and set the terminal characteristics to flush the
	     * output queue (isn't this fun?).
	     */
	    status = setup_tty (fd, FALSE);
	}
	if (debug_flag && (status == SUCCESS) ) {
	    Printf ("Output queue finished draining...\n");
	}
	return (status);
}

/************************************************************************
 *									*
 * flush_tty() - Flush the tty input buffer queue.			*
 *									*
 * Description:								*
 *	This function flushs the tty input buffer queue before starting	*
 * the test.  Ordinarily this isn't necessary, since the driver flushes	*
 * unread characters after closing, but the timer associated with this	*
 * action (on output) and the setting of CLOCAL, may result in unwanted	*
 * characters waiting to be read. In any case, to avoid race conditions	*
 * we'll flush input here to avoid unwarrented data compare failures.	*
 *									*
 * Logic Changed:							*
 *	We only flush the input queue if flushing has been disabled,	*
 * since both the read & write queues are flushed in save_tty().	*
 *									*
 * Inputs:	fd = The terminal file descriptor to flush.		*
 *									*
 * Return Value:							*
 *		void							*
 *									*
 ************************************************************************/
int
flush_tty (int fd)
{
	int status;

	if (!flush_flag && debug_flag) {  /* Flush/display input queue. */
	    ssize_t count;
	    unsigned char buff;

	    if ((status = setup_tty (fd, TRUE)) < 0) {
		return (status);
	    }
	    /*
	     * Read & display any characters in the typeahead buffer.
	     */
	    while ( (count = read (fd, &buff, (size_t) 1)) > (ssize_t) 0) {
		Printf ("Flushing: %c (%d.) (%#x)\n", buff, buff, buff);
	    }
	} else if (!flush_flag) {
	    /*
	     * This could be dangerous if the writer starts first.
	     */
	    if (tcflush (fd, TCIFLUSH) < 0) {	/* Flush input queue. */
		report_error ("tcflush(TCIFLUSH)", TRUE);
	    }
	}
	status = setup_tty (fd, FALSE);
	return (status);
}

/************************************************************************
 *									*
 * save_tty() - Save The Current Terminal Characteristics.		*
 *									*
 * Inputs:	fd = The terminal file descriptor.			*
 *									*
 * Return Value:							*
 *		Returns SUCCESS / FAILURE.				*
 *									*
 ************************************************************************/
int
save_tty (int fd)
{
	int status = SUCCESS;

	if (debug_flag) {
	    Printf ("Saving current terminal characteristics, fd = %d...\n", fd);
	}

#if !defined(_QNX_SOURCE) && !defined(sun) && !defined(__MSDOS__) && !defined(__WIN32__) && !defined(HP_UX)
	/*
	 * Save the line discipline.
	 */
	if ((status = ioctl (fd, TIOCGETD, &saved_ldisc)) < 0) {
	    report_error ("TIOCGETD", FALSE);
	    return (status);
	}
#endif /* !defined(_QNX_SOURCE) */

	/*
	 * Save the terminal characteristics.
	 */
#if defined(sun)
	if ((status = ioctl (fd, TCGETS, &saved_tmodes)) < 0) {
	    report_error ("TCGETS", FALSE);
	}
#elif defined(ultrix)
	if ((status = ioctl (fd, TCGETP, &saved_tmodes)) < 0) {
	    report_error ("TCGETP", FALSE);
	}
#elif defined(_OSF_SOURCE)
	if ((status = ioctl (fd, TIOCGETA, &saved_tmodes)) < 0) {
	    report_error ("TIOCGETA", FALSE);
	}
#elif defined(_QNX_SOURCE) || defined(__MSDOS__) || defined(__WIN32__)
	if ((status = tcgetattr (fd, &saved_tmodes)) < 0) {
	    report_error ("tcgetattr()", FALSE);
	}
#endif /* defined(sun) */
	if (status == SUCCESS) tty_saved = TRUE;
	/*
	 * Since this function is called prior to starting the test,
	 * we'll flush the input & output queues to discard any junk.
	 */
	if (flush_flag) {		/* Allow user to control. */
	    if (tcflush (fd, TCIOFLUSH) < 0) {
		report_error ("tcflush(TCIOFLUSH)", FALSE);
	    }
	}
	return (status);
}

/************************************************************************
 *									*
 * restore_tty() - Restore Saved Terminal Characteristics.		*
 *									*
 * Inputs:	fd = The terminal file descriptor.			*
 *									*
 * Return Value:							*
 *		Returns SUCCESS / FAILURE / WARNING (never saved)	*
 *									*
 ************************************************************************/
int
restore_tty (int fd)
{
	int status = SUCCESS;

	if (tty_saved == FALSE) return (WARNING);

	if (debug_flag) {
	    Printf ("Restoring saved terminal characteristics, fd = %d...\n", fd);
	}

	/*
	 * Restore the saved terminal characteristics.
	 */
#if defined(sun)
	if ((status = ioctl (fd, TCSETSW, &saved_tmodes)) < 0) {
	    report_error ("TCSETSW", TRUE);
	}
#elif defined(ultrix)
	if ((status = ioctl (fd, TCSANOW, &saved_tmodes)) < 0) {
	    report_error ("TCSANOW", TRUE);
	}
#elif defined(_OSF_SOURCE)
	if ((status = ioctl (fd, TIOCSETA, &saved_tmodes)) < 0) {
	    report_error ("TIOCSETA", TRUE);
	}
#elif defined(_QNX_SOURCE) || defined(__MSDOS__) || defined(__WIN32__)
	if ((status = tcsetattr (fd, TCSANOW, &saved_tmodes)) < 0) {
	    report_error ("tcsetattr()", TRUE);
	}
#endif /* defined(sun) */

#if !defined(_QNX_SOURCE) && !defined(sun) && !defined(__MSDOS__) && !defined(__WIN32__) && !defined(HP_UX)
	/*
	 * Restore the saved line discipline.
	 */
	if (ioctl (fd, TIOCSETD, &saved_ldisc) < 0) {
		report_error ("TIOCSETD", TRUE);
	}
#endif /* !defined(_QNX_SOURCE) */
	return (status);
}

/************************************************************************
 *									*
 * setup_tty() - Setup terminal characteristics.			*
 *									*
 * Inputs:	fd = The terminal file descriptor.			*
 *									*
 * Return Value:							*
 *		Returns SUCCESS / FAILURE.				*
 *									*
 ************************************************************************/
int
setup_tty (int fd, int flushing)
{
	int status = SUCCESS;
	struct termios tm;		/* For terminal characteristics	*/
#if !defined(_QNX_SOURCE) && !defined(sun) && !defined(__MSDOS__) && !defined(__WIN32__)
	int ldisc;			/* For line discipline.		*/
#endif

	if (debug_flag && !flushing) {
	    Printf ("Setting up test terminal characteristics, fd = %d...\n", fd);
	}

#if defined(sun)
	/*
	 * If software carrier was detected, then enable it.
	 */
	switch (softcar_opt) {
	    static int on = 1, off = 0;

	    case ON:
		if (ioctl (fd, TIOCSSOFTCAR, &on) < 0) {
		    report_error ("TIOCSOFTCAR", FALSE);
		}
		break;

	    case OFF:
		if (ioctl (fd, TIOCSSOFTCAR, &off) < 0) {
		    report_error ("TIOCSOFTCAR", FALSE);
		}
		break;
	}
#endif /* defined(sun) */

#if !defined(_QNX_SOURCE) && !defined(sun) && !defined(__MSDOS__) && !defined(__WIN32__) && !defined(SCO) && !defined(HP_UX) && !defined(AIX)
	/*
	 * Ensure the correct line discipline is setup.
	 */
	if ((status = ioctl (fd, TIOCGETD, &ldisc)) < 0) {
	    report_error ("TIOCGETD", FALSE);
	    return (status);
	}
#if defined(ultrix)
	if (ldisc != TERMIODISC) {
	    ldisc = TERMIODISC;
	    if ((status = ioctl (fd, TIOCSETD, &ldisc)) < 0) {
		report_error ("TIOCSETD", FALSE);
		return (status);
	    }
	}
#elif defined(__linux__)
	if (ldisc != N_TTY) {
	    ldisc = N_TTY;
	    if ((status = ioctl (fd, TIOCSETD, &ldisc)) < 0) {
		report_error ("TIOCSETD", FALSE);
	        return (status);
	    }
	}
#else /* !defined(ultrix) */
	if (ldisc != TTYDISC) {
	    ldisc = TTYDISC;
	    if ((status = ioctl (fd, TIOCSETD, &ldisc)) < 0) {
		report_error ("TIOCSETD", FALSE);
		return (status);
	    }
	}
#endif /* defined(ultrix) */
#endif /* !defined(_QNX_SOURCE) */

	/*
	 * For terminals, get and set the terminal characteristics.
	 */
#if defined(sun)
	if ((status = ioctl (fd, TCGETS, &tm)) < 0) {
	    report_error ("TCGETS", FALSE);
	    return (status);
	}
#elif defined(ultrix)
	if ((status = ioctl (fd, TCGETP, &tm)) < 0) {
	    report_error ("TCGETP", FALSE);
	    return (status);
	}
#elif defined(_OSF_SOURCE)
	if ((status = ioctl (fd, TIOCGETA, &tm)) < 0) {
	    report_error ("TIOCGETA", FALSE);
	    return (status);
	}
#elif defined(_QNX_SOURCE) || defined(__MSDOS__) || defined(__WIN32__)
	if ((status = tcgetattr (fd, &tm) < 0)) {
	    report_error ("tcgetattr()", FALSE);
	    return (status);
	}
#endif /* defined(sun) */
	tm.c_cflag = tm.c_iflag = tm.c_oflag = tm.c_lflag = 0;
	if (modem_flag) {
	    tm.c_cflag = HUPCL;		/* Hangup on last close. */
	} else {
	    tm.c_cflag = CLOCAL;	/* Ignore modem signals. */
	}
#if defined(sun) || defined(__linux__) || defined(SCO) || defined(HP_UX) || defined(AIX)
	tm.c_cflag |= (baud_rate_code | data_bits_code | parity_code | CREAD);
#else /* !defined(sun) */
	tm.c_cflag |= (data_bits_code | parity_code | CREAD);
#if defined(ultrix)
	tm.c_cflag |= ( (baud_rate_code << 16) | baud_rate_code);
#else /* !defined(ultrix) */
	tm.c_ispeed = tm.c_ospeed = baud_rate_code;
#endif /* defined(ultrix) */
#endif /* defined(sun) */

	/*
	 * Send two stop bits for slower speeds (is this right?).
	 */
	if (baud_rate_code < B300) {
	    tm.c_cflag |= CSTOPB;
	}

	/*
	 * Set VMIN & VTIME values, checking for maximum values.
	 */
	if (flushing) {
	    tm.c_cc[VMIN] = 0;
	} else if (tty_minflag) {
	    tm.c_cc[VMIN] = (tty_minimum > VMIN_MAX) ? VMIN_MAX
						     : tty_minimum;
	} else {
	    tm.c_cc[VMIN] = (block_size > VMIN_MAX) ? VMIN_MAX
						    : block_size;
	}

	if (flushing) {
	    tm.c_cc[VTIME] = 1;
	} else {
	    tm.c_cc[VTIME] = (tty_timeout > VTIME_MAX) ? VTIME_MAX
						       : tty_timeout;
	}

	/*
	 * Set the desired flow control.
	 */
#if defined(sun) || defined(_OSF_SOURCE) || defined(__linux__)
	if (flow_type == CTS_RTS) {
	    tm.c_cflag |= CRTSCTS;		/* CTS/RTS flow control. */
	} else if (flow_type == XON_XOFF) {
	    tm.c_iflag |= (IXON | IXOFF);	/* XON/XOFF Flow control. */
	}
#elif defined(_QNX_SOURCE)
	tm.c_lflag |= IEXTEN;			/* QNX POSIX extensions. */
	if (flow_type == CTS_RTS) {
	    tm.c_lflag |= (IHFLOW | OHFLOW);	/* CTS/RTS flow control. */
	} else if (flow_type == XON_XOFF) {
	    tm.c_iflag |= (IXON | IXOFF);	/* XON/XOFF Flow control. */
	}
#else /* Ultrix, POSIX, and all others (I hope)... */
	if (flow_type == XON_XOFF) {
	    tm.c_iflag |= (IXON | IXOFF);	/* XON/XOFF Flow control. */
	}

#endif /* defined(sun) || defined(_OSF_SOURCE) */

	/*
	 * If 7 bit data, or parity is being enabled, enable stripping
	 * of the eight data bit, otherwise the driver passes it back.
	 * We don't test generation of correct parity, driver does this.
	 */
	if ( (data_bits_code == CS7) || (parity_code & PARENB) ) {
	    tm.c_iflag |= ISTRIP;		/* Strip the 8th data bit */
	    /*
	     * For parity, enable input parity checking.
	     */
	    if (parity_code & PARENB) {
		tm.c_iflag |= INPCK;		/* Check input parity.	*/
	    }
	}

	/*
	 * Setup the terminal characteristics after output is done.
	 */
#if defined(sun)
	if ((status = ioctl (fd, TCSETSW, &tm)) < 0) {
	    report_error ("TCSETSW", FALSE);
	    return (status);
	}
#elif defined(ultrix)
	if ((status = ioctl (fd, TCSADRAIN, &tm)) < 0) {
	    report_error ("TCSADRAIN", FALSE);
	    return (status);
	}
#elif defined(_OSF_SOURCE)
	if ((status = ioctl (fd, TIOCSETAW, &tm)) < 0) {
	    report_error ("TIOCSETAW", FALSE);
	    return (status);
	}
#elif defined(_QNX_SOURCE) || defined(__MSDOS__) || defined(__WIN32__)
	if ((status = tcsetattr (fd, TCSADRAIN, &tm)) < 0) {
	    report_error ("tcsetattr()", FALSE);
	    return (status);
	}
#endif /* defined(sun) */

	/*
	 * For testing modem lines, wait for modem to be ready.
	 */
	if (modem_flag) {
#if defined(_OSF_SOURCE) || defined(ultrix)
	    if (debug_flag) (void) ShowModemSignals (fd);
#if 0
	    status = WaitForCarrier (fd);
	    if (debug_flag) (void) ShowModemSignals (fd);
	    status = (status == TRUE) ? SUCCESS : FAILURE;
	    if (status == SUCCESS) {
		status = SetBlockingMode (fd);
#endif
#endif /* defined(_OSF_SOURCE) || defined(ultrix) */
	    /*
	     * Don't wait for modem signals, simply reset non-blocking
	     * mode, and let the first read or write system call block.
	     * [ NOTE: I've changed this logic for direct lines, since
	     *   I'm seeing the writer causing CTS/DTS/Carrier to set.
	     *   Plus, this code should work on all operating systems. ]
	     */
	    status = SetBlockingMode (fd);
	}
	return (status);
}

/************************************************************************
 *									*
 * setup_baud_rate() - Setup the baud rate code.			*
 *									*
 * Description:								*
 *	This function takes a specified baud rate (i.e. 9600) and maps	*
 * it to the baud rate code we must specify to the Unix terminal driver	*
 * to set that baud rate.						*
 *									*
 * Inputs:	baud = Pointer to baud rate string.			*
 *									*
 * Return Value:							*
 *		Returns SUCCESS / FAILURE = Valid Speed/Invalid Speed.	*
 *									*
 ************************************************************************/
int
setup_baud_rate (u_int32 baud)
{
    int i;
    struct tty_baud_rate *tsp;

    for (tsp = baud_rate_tbl, i = 0; i < num_baud_rates; i++, tsp++) {
	if (baud == tsp->usr_speed) {
	    baud_rate_code = tsp->tty_speed;	/* Save baud rate code. */
	    return (SUCCESS);			/* Return success status. */
	}
    }
    fprintf (efp, "Baud rate '%d' is invalid, valid entrys are:\n", baud);
    for (tsp = baud_rate_tbl, i = 0; i < num_baud_rates; i++, tsp++) {
	if ( (i % 6) == 0) fprintf (efp, "\n");
	fprintf (efp, "%10d", tsp->usr_speed);
    }
    fprintf (efp, "\n");
    return (FAILURE);
}

int
SetBlockingMode (int fd)
{
	int flags;

	if ( (flags = fcntl (fd, F_GETFL)) == FAILURE) {
		perror ("fcntl(F_GETFL)");
		return (FAILURE);
	}
	/*
	 * BEWARE: O_NDELAY & O_NONBLOCK are _not_ the same value anymore.
	 * [ NOTE: O_NONBLOCK is defined by POSIX (O_NDELAY is _not_. ]
	 */
	flags &= ~(O_NONBLOCK);
	if ( (fcntl (fd, F_SETFL, flags)) == FAILURE) {
		perror ("fcntl(F_SETFL)");
		return (FAILURE);
	}
	return (SUCCESS);
}

#if defined(_OSF_SOURCE) || defined(ultrix)

/*
 * To the best of my knowledge, this is Digital Unix (OSF/Ultrix) specific:
 */


unsigned int
GetModemSignals (int fd)
{
	unsigned int msigs;

	if (ioctl(fd, TIOCMGET, &msigs) < 0) {
		perror("TIOCMGET");
		return(FAILURE);
	}
	return (msigs);
}

int
SetModemSignals (int fd, int msigs)
{
	if (ioctl (fd, TIOCMBIS, &msigs) < 0) {
		perror("TIOCMBIS");
		return(FAILURE);
	}
	return (SUCCESS);
}

int
HangupModem (int fd)
{
	int status;
	unsigned int delay = 3;

	if ((status = ioctl(fd, TIOCCDTR, 0)) < 0) perror("TIOCCDTR");
	sleep(delay);
	if ((status = ioctl(fd, TIOCSDTR, 0)) < 0) perror("TIOCSDTR");
	sleep(1);
	return (status);
}

#define P(fmtstr)		fprintf (efp, fmtstr)
#define P1(fmtstr,arg)		fprintf (efp, fmtstr, arg)

int
ShowModemSignals (int fd)
{
	unsigned int msigs;

	if ( (msigs = GetModemSignals(fd)) == FAILURE) {
		return(FAILURE);
	}
	P ("--------------------------------------------------\r\n");
	P1("Modem Signals Set: 0x%x\r\n", msigs);
	if (msigs & TIOCM_LE) {
	  P1("   0x%x = TIOCM_LE = Line Enable.\r\n", TIOCM_LE);
	}
	if (msigs & TIOCM_DTR) {
	  P1("   0x%x = TIOCM_DTR = Data Terminal Ready.\r\n", TIOCM_DTR);
	}
	if (msigs & TIOCM_RTS) {
	  P1("   0x%x = TIOCM_RTS = Request To Send.\r\n", TIOCM_RTS);
	}
	if (msigs & TIOCM_ST) {
	  P1("   0x%x = TIOCM_ST = Secondary Transmit.\r\n", TIOCM_ST);
	}
	if (msigs & TIOCM_SR) {
	  P1("   0x%x = TIOCM_SR = Secondary Receive.\r\n", TIOCM_SR);
	}
	if (msigs & TIOCM_CTS) {
	  P1("   0x%x = TIOCM_CTS = Clear To Send.\r\n", TIOCM_CTS);
	}
	if (msigs & TIOCM_CAR) {
	  P1("   0x%x = TIOCM_CAR = Carrier Detect.\r\n", TIOCM_CAR);
	}
	if (msigs & TIOCM_RNG) {
	  P1("   0x%x = TIOCM_RNG = Ring Indicator.\r\n", TIOCM_RNG);
	}
	if (msigs & TIOCM_DSR) {
	  P1("   0x%x = TIOCM_DSR = Data Set Ready.\r\n", TIOCM_DSR);
	}
	P ("--------------------------------------------------\r\n");
	return(SUCCESS);
}

int
WaitForCarrier (int fd)
{
	unsigned int msigs;
	unsigned int delay = 1;

	if (debug_flag) {
	    Printf ("Waiting for carrier or DSR signals...\n");
	}
	do {
	    if ( (msigs = GetModemSignals(fd)) == FAILURE) {
		return(FALSE);
	    }
	    sleep (delay);
	} while ( (msigs & (TIOCM_CAR|TIOCM_DSR)) == 0);
	return (TRUE);
}

#endif /* defined(_OSF_SOURCE) || defined(ultrix) */


syntax highlighted by Code2HTML, v. 0.9.1