5

I am reading data through a USB connection as a serial port with the PL2303 driver. It returns successfully when doing an open and when I set they TTY options and non blocking. When I try to close the connection, it hangs. In this state it reads "�" instead of characters.

I can connect to the device perfectly fine with cutecom. Here is the strange part:

  1. If I first connect to the device via cutecom (a serial monitor), my program will connect and close perfectly fine every time afterwards. It reads the characters as I expect them to be read. (No �).
  2. If I disconnect and reconnect the hardware, my program will hang again until I run cutecom.

Since it works after I use cutecom, it makes me think that I am missing something in my initial connection, or connection settings. Here's what I use to connect:

baud_rate = 38400;
fd =  open (device_path, O_RDONLY | O_NOCTTY );

In my set_tty_options function:

struct termios tty_options;

memset (&tty_options, 0, sizeof(tty_options));
tcgetattr (fd, &tty_options);

cfsetispeed(&tty_options, baud_rate);                         // set baud rate
tty_options.c_cflag = (tty_options.c_cflag & ~CSIZE) | CS8;   // 8 bit msgs
tty_options.c_cflag |= (CLOCAL | CREAD);                      // enable reading

tty_options.c_cflag &= ~(PARENB | PARODD);                    // shut off parity
tty_options.c_cflag |= parity;
tty_options.c_cflag &= ~CSTOPB;
tty_options.c_cflag &= ~CRTSCTS;

if (tcsetattr (fd, TCSANOW, &tty_options) != 0) 
{
  printf("error %d from tcsetattr\n", errno);
  return TTY_ERROR;
}

In set_blocking function:

if (tcgetattr (fd, &tty) != 0)
{
  printf("error %d from tggetattr", errno);
  return FAILURE;
}

// 0 or 1 byte is enough to return from read
tty.c_cc[VMIN]  = should_block ? 1 : 0; 
tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

if (tcsetattr (fd, TCSANOW, &tty) != 0) 
{
  printf("error %d setting term attributes", errno);
  return FAILURE;
}
Nate
  • 1,889
  • 1
  • 20
  • 40
  • I see 2 problems with your code. First is that `set_tty_options` function does not seem to fully initialize the `tty_options` structure. That could explain the "my program works if I run X first, but hangs/fails if run solo." That's the classic symptom of a program that does not properly or fully initialize its environment. Second, that `set_blocking` function is bogus for canonical input. `c_cc[VMIN]` and `c_cc[VTIME]` should only be used for **non-canonical** (aka raw) input. For a non-blocking `read()` of canonical input, use `fcntl()` to set that up. – sawdust Apr 03 '13 at 10:17

2 Answers2

1

I think you want to add | O_SYNC to the open flags to insist on synchronous i/o. I doubt that is causing a problem though.

However, I think you want to ignore the break signal, which is reported as a NUL character like you are getting:

tty_settings.c_iflag &= ~IGNBRK;         // ignore break signal

Also, you want to be sure the input processing is completely turned off, so that receipt of a backspace, ^C, ^\, etc. aren't triggering any reaction:

tty_settings.c_lflag = 0;                // no signaling chars, no echo,
                                         // no canonical processing

It looks like you are already using my set_blocking() function, so that should be okay.

Community
  • 1
  • 1
wallyk
  • 56,922
  • 16
  • 83
  • 148
0

Here's what I ended up doing. I figured this out by basically copying and pasting parts from cutecom's source code.

  1. When opening...

    int fd, n;
    fd = open (device_path, O_RDONLY | O_NOCTTY | O_NDELAY);
    
    ... error check fd ...
    
    n = fcntl(ail_info->ail_serial_fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, n & ~O_NDELAY);
    
  2. You cannot set the baud rate as I was doing. You have to use the defined B38400;

    baud = B38400;

  3. Then, I added wallyk's answer.

    tty_settings.c_lflag = 0;

Edit: As per sawdust comment, I found a better way to set this to raw input.

tty_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

And it works.

Nate
  • 1,889
  • 1
  • 20
  • 40
  • *"Then, I added wallyk's answer. tty_settings.c_lflag = 0;"* -- You still have not properly configured the serial port. What is `c_oflag` set to? Note that hard assignment to `termios` struct members is not recommended per POSIX. You should call `tcgetattr()`, and then enable or disable each attribute field (as you do for `c_cflag`). See [Serial Programming Guide for POSIX Operating Systems](http://www.easysw.com/~mike/serial/serial.html). You are using POSIX calls, so follow their rules. – sawdust Apr 03 '13 at 20:34