1

I am new to the linux programming. I followed example on the web to read/write to console e.g., "/dev/ttyS0". Each time I run the code, it exits without prompting user to write the input. It also distorts the terminal prompt (newline) and I am unable to see what I am typing... Here is the code I am using :

int main(int argc, char** argv)
{
  struct termios tio;
  struct termios stdio;
  int tty_fd;
  /* fd_set rdset; */

  printf("Please start with %s /dev/ttyS0 (for example)\n",argv[0]);
  unsigned char mesg='D';

  memset(&stdio,0,sizeof(stdio));
  stdio.c_iflag=0;
  stdio.c_oflag=0;
  stdio.c_cflag=0;
  stdio.c_lflag=0;
  stdio.c_cc[VMIN]=1;
  stdio.c_cc[VTIME]=0;
  tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
  tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
  fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);       // make the reads non-blocking

  memset(&tio,0,sizeof(tio));
  tio.c_iflag=0;
  tio.c_oflag=0;
  tio.c_cflag=CS8|CREAD|CLOCAL;           // 8n1, see termios.h for more information
  tio.c_lflag=0;
  tio.c_cc[VMIN]=1;
  tio.c_cc[VTIME]=5;

  tty_fd=open(argv[1], O_RDWR | O_NONBLOCK | O_NOCTTY);
  cfsetospeed(&tio,B115200);            // 115200 baud
  cfsetispeed(&tio,B115200);            // 115200 baud

  tcsetattr(tty_fd,TCSANOW,&tio);
  while (mesg != 'q') {
    if (read(tty_fd,&mesg,1)>0)        write(STDOUT_FILENO,&mesg,1);              // if new data is available on the serial port, print it out
    if (read(STDIN_FILENO,&mesg,1)>0)  write(tty_fd,&mesg,1);                     // if new data is available on the console, send it to the serial port
  }

  close(tty_fd);

  return(0);
}
iamauser
  • 11,119
  • 5
  • 34
  • 52
  • *"I followed example on the web..."* -- Then you may be using a bad example. Calling `memset( , 0, )` and then assigning 0 to the structure elements is redundant and could cause problems. Proper practice per POSIX is to call `tcgetattr()` and then modify the individual attributes. See [Serial Programming Guide for POSIX Operating Systems](http://www.cmrr.umn.edu/~strupp/serial.html). Zeroing out the attributes for `stdout` is certainly wrong and going to cause problems – sawdust May 15 '13 at 02:12

1 Answers1

1

When you're experimenting with code that changes tty parameters, you should wrap your invocations of the test program with a script which saves and restores the parameters, in case your program neglects to do so.

This can be done like this:

 $ SAVED_TTY=$(stty -g)          # save the good settings once
 $ ./a.out ; stty $SAVED_TTY     # restore than after each run of the program

The -g option of stty causes it to output all of the tty settings "pickled" into a character string, which is acceptable as an argument to a future invocation of stty to restore the same settings.

(Properly written tty-manipulating programs take care to restore the terminal settings when they exit, and even if they exit abruptly by receiving any fatal signal that can be handled.)

As for the question of how to loop back what is written to the tty device, there is no general functionality for this in the tty subsystem itself. The standard tty line discipline module can echo incoming characters back to the output, so that when users use line-oriented programs, they can see their own typing, but there is no software loopback whereby a tty device pretends to receive some characters that it has just sent.

However, some serial hardware is capable of hardware loopback: essentially tying the UART TX line to the RX line for the purpose of testing.

The Linux tty's support a modem control ioctl which can be used to turn this on and off, if the hardware supports it. This takes the form of TIOCMGET and TIOCMSET ioctls. These ioctls work with a value that is a logical OR of various masks, one of which is TIOCM_LOOP.

So, I believe that setting up hardware loopback goes something like this:

unsigned int modem_control_bits;

result = ioctl(tty_descriptor, TIOCMGET, &modem_control_bits);
/* check result for error, of course */

modem_control_bits |= TIOCM_LOOP; /* request loopback from serial port */

result = ioctl(tty_descriptor, TIOCMSET, &modem_control_bits);
/* check result for error */

It's not clear whether every serial driver will report an error if it is asked to set TIOCM_LOOP, but the hardware does not support it. (I don't think that just because the result is zero, it necessarily worked).

There are other TIOCM_* bits, so you can do standard things like turn on and off DTR, or detect whether the ring indicator is on and whatnot.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • You should put some small sub-second sleep in the loop, otherwise it spins at 100% CPU through the non-blocking reads. – Kaz May 14 '13 at 20:16
  • are you talking about the `while` loop in the code ? btw, how do I read back the characters I just wrote on the terminal "/dev/ttyS0". – iamauser May 14 '13 at 20:44
  • Yes, the only loop in the code. What do you mean by "read back the characters"? `/dev/ttyS0` sends characters to a serial port. They don't come back unless you put the serial port in loopback mode, or you have some external hardware that loops characters back into the same port, or another port. – Kaz May 14 '13 at 20:47
  • I meant echo back what I wrote. Will you suggest some syntax to do that ? – iamauser May 14 '13 at 22:52
  • I will add that to the answer. – Kaz May 15 '13 at 01:10