TTY line discipline

Mike Verstegen mdv at comtst.UUCP
Tue Jul 24 01:40:52 AEST 1990


In article <2026 at beam.UUCP>, lucio at beam.UUCP (Lucio de Re) writes:
> 
> We first encountered this problem in Xenix (System V, Version 2.3.2), but
> experimentation showed that it occurs in Intel Unix System V, Release 3.2.2
> as well.
> 
> The symptoms are as follows: using the standard (and only?) line
> discipline, the following termio settings had a strange side effect:
> 
>     n_ctrl.c_iflag &= ~(INLCR | IGNCR | ICRNL | IUCLC );
>     n_ctrl.c_iflag |= IXOFF;
>     n_ctrl.c_lflag &= ~(ICANON | ISIG);
>     n_ctrl.c_cc[VINTR] = 255;   /*  these seem to disable ...  */
>     n_ctrl.c_cc[VQUIT] = 255;   /*  ...                        */
>     n_ctrl.c_cc[VERASE] = 255;  /*  ...                        */
>     n_ctrl.c_cc[VKILL] = 255;   /*  ... the relevant function  */
>     n_ctrl.c_cc[VMIN] = 1;      /*  character count         */
>     n_ctrl.c_cc[VTIME] = 50;    /*  time limit              */
> 
> that is, a null character on input would trigger the timeout
> condition. As a result, the null is swallowed by the line discipline
> and cannot be received. If the timeout is set to zero, the problem
> does not arise, and nulls are accepted as normal characters.
> 
[ Remainder of post deleted ]

We have been using a set up similar to this for quite a while (7 years) and
have learned a few things about using async line to do what it appears that
you are trying to do.

DISCLAIMER:
    I've never read the source to any kernel code and the following
    come only from public doucmentataion and a lot of experience with different
    combinations. Corrections from more knowledgable sources welcome.

Once signal processing is turned off with ~SIG, the input characters
are not checked for the INTR, SWITCH, and QUIT characters. Similarly,
when cannonical is turned off with ~ICANON, no checks are made for the
ERASE and KILL characters. the input characters are now unchecked and
reads will be satified directly from the input buffer subject to the
limitation that no read will be satisfied until 
	1) At least MIN characters have been received AND
	2) The timeout value TIME has expired ***BETWEEN CHARACTERS***
Note that there is no timeout for the initial character. This is important!

For our application, we have written a module that uses some internal buffering
and signals, in conjuntion with VMIN and VTIME settings to provide efficient
communications. A code fragment follows which implements this strategy:

/*****************************************************************************/
/* eread_time -- efficient reading mechanism for single characters from a
	tty port. This function is intended to be a replacement for the current
	read_time which does a read call for each individual character.
	This function uses an internal buffer and reads a block of characters
	which are buffered (internally to the module) and returned with each
	successive call. This approach significantly decreases the number of
	read calls made, and the corresponding overhead associated with kernel
	calls is therefore minimized.

	Input:	fd	file descriptor of a currently opened file (see Note)
		timeout	the time (in seconds) to wait for the character
		c	pointer to the location of the read character
	Return:	SUCCESS	if a character was available and c is set to that char
		FAILURE	if a character was not available and c is undefined

	Note:	Prior to the first call to eread_time, an ioctl call must be
		made to turn off the cannonical processor, set VMIN to 10 (this
		value may require tuning) and VMIN to BUFSIZE.

	History:Mike Verstegen 10/26/87 -- rewritten from scratch
*/

#define BUFSIZE 64

eread_time(fd, timeout, c)
int	fd;
int	timeout;
char	*c;
{
static
char	buf[BUFSIZE];
static
char	*bufend = buf;		/* point to the last valid char in the buffer */
static
char	*outptr = buf + 1;	/* points to the next char to return */
				/* this initialization force a read first time
					through */
unsigned leftover;		/* time leftover on the timer */
int	ret;			/* return value from read call */
sig_t	(*save_sig)();		/* temporary variable for saving current signal
					handler */
sig_t	null_fn();		/* declaration for null function defined below*/

if (outptr <= bufend)		/* can current request be satisfied from bufr?*/
    {
    *c = *outptr++;
    return(SUCCESS);
    }
else				/* empty buffer, do a real read */
    {
    save_sig = signal (SIGALRM, null_fn);	/* save current signal handler*/
    leftover = alarm ((unsigned) timeout);	/* and timer */

    ret = read(fd, buf, (unsigned)BUFSIZE);	/* get the characters */
    
    (void)signal(SIGALRM, save_sig);		/* restore signal handler */
    (void)alarm(leftover);			/* and timer */

    if (ret < 0)				/* did the read succeed ? */
	{
	if (errno != EINTR)			/* if not an interrupt call...*/
	    perror("eread_time");		/* we've got a problem */
	return (FAILURE);			/* otherwise, just a timeout */
	}
    else
	{
	outptr = buf;				/* at beginning of buffer */
	bufend = buf + ret - 1;			/* end offset by how many read*/
	*c = *outptr++;				/* the first char is returned */
	}
    }
return (SUCCESS);
}

sig_t						/* null functiof for signal */
null_fn() {}

#ifdef TEST
/*****************************************************************************/
/* a short test section to read from the standard input. To work successfully,
   the user must type "stty -icanon eol ^c eof ^c" to get in to cbreak mode.
*/
main ()
{

char	c;

while (1)
    {
    if (eread_time(0, 2, &c) == FAILURE)
	printf ("FAIL\n");
    else
	printf ("OK -- '%c'\n", c);
    }
exit (1);
}
#endif

------------------------- End of code fragment ---------------------------

I hope this is helpful. You should also refer to termio(7) of you Unix manuals
for a more detailed discussion of tty flags usage.
--
Mike Verstegen
..!uunet!comtst!mdv
mdv at domain.com



More information about the Comp.unix.wizards mailing list