1
votes

I have a USB serial port module and I want to use it in my C application. The problem is the open system call doesn't return!

int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY); // doesn't return

I added my user to dialout group and I can access the port without root access using minicom in terminal. So it's not permission problem. Even if it is, it should return instantly and give permission error. But open function doesn't return at all.

What should I do? Do you have any idea?

Thanks

2

2 Answers

2
votes

I believe this can happen if the device is busy, etc..

You need to add the flag O_NONBLOCK:

O_NONBLOCK When opening a FIFO with O_RDONLY or O_WRONLY set: * If O_NONBLOCK is set, an open() for reading-only shall return without delay. An open() for writing-only shall return an error if no process currently has the file open for reading. * If O_NONBLOCK is clear, an open() for reading-only shall block the calling thread until a thread opens the file for writing. An open() for writing-only shall block the calling thread until a thread opens the file for reading.

When opening a block special or character special file that supports non-blocking opens:

*
    If O_NONBLOCK is set, the open() function shall return without blocking for the device to be ready or available. Subsequent behavior

of the device is device-specific. * If O_NONBLOCK is clear, the open() function shall block the calling thread until the device is ready or available before returning.

Otherwise, the behavior of O_NONBLOCK is unspecified.

2
votes

int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

In addition to @darune's answer and O_NONBLOCK, you might consider O_SYNC too. Also see How to open, read, and write from serial port in C?

You might also consider making the file descriptor exclusive so another program like Modem Manager does not open the device and muck with your state. Exclusive is fine because of O_RDWR. Also see How to make /dev/ttyACM0 (and friends) exclusive? on the Kernel Newbies mailing list.

To make the file descriptor exclusive you need to use ioctl and TIOCEXCL. O_EXCL does not work as expected because it is not honored for character devices (and the kernel folks say -ENOPATCH).

int term_config(int fd, int speed)
{
    struct termios tty;
    memset(&tty, 0, sizeof(tty));

    if (tcgetattr(fd, &tty) != 0) {
        log_error("term_config: tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfmakeraw(&tty);
    tty.c_cflag |= CLOCAL;   /* ignore status lines   */
    tty.c_cflag |= CRTSCTS;  /* hardware flow control */

    cfsetospeed(&tty,(speed_t)speed);
    cfsetispeed(&tty,(speed_t)speed);

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        log_error("term_config: tcsetattr: %s\n", strerror(errno));
        return -1;
    }

    if (ioctl(fd, TIOCEXCL, NULL) != 0) {
        log_error("term_config: ioctl_tty: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

You would call term_config something like:

int serialDevice = open("/dev/ttyUSB0", ...);
if (serialDevice == -1) { /* error */ }

int result = term_config(serialDevice, B115200);
if (result != 0) { /* error */ }