From: scatter@scubed.com (McLaughlin)
Subject: ioctl() on the SPARC 
Followup-To: comp.sys.sun.misc
Keywords: ioctl termio 
Reply-To: scatter@scubed.com
Organization: S-CUBED Division of Maxwell Laboratories
Date: Wed, 6 Jan 1993 02:11:51 GMT

I am controling a device over one of an RS-232 line on an IPC.
I am having difficulties.

My problem is that ioctl() does not seem to be doing what the man
page(s) advertise.  Of coarse, it is a good possibility that I am 
screwed up and the problem is pilot error on my part.  If there is
some lore and wisdom out there in netland, I would gladly like to
find out now before I spend more time beating my head against the
wall.  I have a version of the code working on an SGI, so I know that
the "Non-Canonical Mode Input Processing" works on that machine.

1) Am I using ioctl() incorrectly?
2) Is ioctl() broken under SUNOS?
3) Will my code be broken again when we go to SOLARIS?
4) Was termio(4) and ioctl invented for some programmer's job security?
5) Am I going to have to write my own interupts?

The root of my problem is that according to the termio(4) man page I should be
able to use "Non-Canonical Mode Input Processing" and set MIN and TIME.
I am interested in Case C: MIN = 0, TIME > 0 and  Case D: MIN = 0, TIME = 0.
The termio(4) man page says:

".....
  Case C: MIN = 0, TIME > 0
     In this case, since MIN = 0, TIME no  longer  represents  an
     intercharacter timer.  It now serves as a read timer that is
     activated as soon as a read() is done.  A read is  satisfied
     as  soon as a single character is received or the read timer
     expires.  Note: in this case if the timer expires, no  char-
     acter  will  be returned.  If the timer does not expire, the
     only way the read can be satisfied  is  if  a  character  is
     received.  In this case the read will not block indefinitely
     waiting for a character - if no character is received within
     TIME*.10  seconds after the read is initiated, the read will
     return with zero characters.

  Case D: MIN = 0, TIME = 0
     In this case return is immediate.  The minimum of either the
     number  of  characters requested or the number of characters
     currently available will be  returned  without  waiting  for
     more characters to be input.
....."

My problem is that no matter how long I wait, whether MIN=0 or 1, my read() 
never returns if the external device has not transmitted a character.
It does not matter whether TIME = 0 or 1.  It is working as if TIME = INFINITY.

My routine, lvr_init(), for opening the port is listed below.  

/* a whole bunch of includes */
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <sgtty.h>
#include <fcntl.h>
#include <errno.h>
/* Global Static Variables */
static struct termios new_port_d2;
static struct termios old_port_d2;
static struct sgttyb old_port, new_port;
static struct tchars new_tchars, old_tchars;
static struct ltchars new_lchars, old_lchars;
/* the pointer to the LVR tyy port */
static int fdes;
/* termio and sgtty stuff */
int ldisc = NTTYDISC;
int local_mode = 0;

int lvr_init (tty)  /* initialize communications with lvr */
     char * tty;    /* device name such as /dev/ttyb */
{
  extern int errno;
  int i;
  int ioctl_err;

  /* opening port */
  if ((fdes = open (tty, O_RDWR | O_NDELAY | O_NOCTTY )) == -1) {
      fprintf (stderr, "lvr_init error: cannot open tty port %s.\n", tty);
      return (-1);
    }

  /* get termio struct for device */
  if ((ioctl_err = ioctl (fdes, TCGETS, &old_port_d2)) == -1) {
      fprintf (stderr, "lvr_init error: old termio struct. fdes =%d\n", fdes);
      close (fdes);
      return (-1);
    }

  /* initialize new termio struct */
  new_port_d2.c_iflag = IGNPAR;	/* input flag */
  new_port_d2.c_oflag = 0;	/* output flag */
  new_port_d2.c_cflag = B9600 | CS8 | CLOCAL;	/* 9600 Baud and 8 bits */
  new_port_d2.c_lflag = 0;	/* local modes */
  new_port_d2.c_line = old_port_d2.c_line;
  for (i = 0; i < NCCS; i++)
    new_port_d2.c_cc[i] = old_port_d2.c_cc[i];

  /* required for non-cononical input mode processing */
  /* VMIN is minimum number of characters for the read to satisfy */
  /* VTIME is in units of 0.10 sec */
  /* setting VEOF or VMIN */
  new_port_d2.c_cc[4] = 0;
  /* setting VEOL or VTIME */
  /* this does not work,...... */
  new_port_d2.c_cc[5] = 1;
  /* this sort of works but is not what I want........ */
  new_port_d2.c_cc[5] = 0;

  if ((ioctl_err = ioctl (fdes, TCSETS, &new_port_d2)) == -1) {
      fprintf (stderr, "lvr_init: new termio struct. fdes =%d\n", fdes);
      close (fdes);
      return (-1);
    }

  /* Get current SGTTYB struct for /dev/tty* */
  if ((ioctl_err = ioctl (fdes, TIOCGETP, &old_port)) == -1) {
      fprintf (stderr, "LVR_IOCTL_SGTTY\n");
      return (-1);
    }

  /* Get current TCHARS struct for /dev/tty* */
  if ((ioctl_err = ioctl (fdes, TIOCGETC, &old_tchars)) == -1) {
      fprintf (stderr, "LVR_IOCTL_TCHARS\n");
      return (-1);
    }

  /* Initialize new SGTTYB struct */
  new_port.sg_ispeed = B9600;	/* 9600 baud input */
  new_port.sg_ospeed = B9600;	/* 9600 baud output */
  new_port.sg_flags = RAW;	/* Raw mode */

  if ((ioctl_err = ioctl (fdes, TIOCSETP, &new_port)) == -1) {
      fprintf(stderr, "lvr_init: setting sgtty\n");
      close (fdes);
      return (-1);
    }

  /* Initialize new line discipline */
  if ((ioctl_err = ioctl (fdes, TIOCSETD, &ldisc)) == -1) {
      fprintf(stderr, "lvr_init: ldisc\n");
      close (fdes);
      return (-1);
    }

  /* Set special characters */
  if ((ioctl_err = ioctl (fdes, TIOCSETC, &new_tchars)) == -1) {
      fprintf(stderr, "lvr_init: spec chars\n");
      close (fdes);
      return (-1);
    }

  /* Set local modes */
  if ((ioctl_err = ioctl (fdes, TIOCLBIS, &local_mode)) == -1) {
      fprintf(stderr, "lvr_init: local mode\n");
      close (fdes);
      return (-1);
    }

  if ((ioctl_err = ioctl (fdes, TIOCSLTC, &new_lchars)) == -1) {
      fprintf(stderr, "lvr_init: local spec\n");
      close (fdes);
      return (-1);
    }

  /* Flush input and output buffers just for drill */
  if ((ioctl_err = ioctl (fdes, TIOCFLUSH, 0)) == -1) {
      fprintf(stderr, "lvr_init: FLUSH_PORT\n");
      return (-1);
    }

  return (0);
}				/* end lvr_init */


-- 
Keith L. McLaughlin (scatter)
e-mail: scatter@vulcan.scubed.com, scatter@seismo.CSS.GOV
phone: 619-587-8436, 619-453-0060, FAX 619-755-0474
snail-mail: S-CUBED, Div. Maxwell Laboratories, POB 1620, La Jolla, CA 92038