1

I'm currently trying to communicate with an external device using the serial port, and it works just fine... if the device is connected. However, since there is no guarantee that it is (and i have multiple serial ports to choose from), it would be ideal if i could use the VMIN=0 / VTIME>0 timed read to probe the ports (and generally prevent my application from blocking indefinitely, in case the device is detached during operation).

Here is my code to open a serial port, and set it to noncanonical mode. But even though i set VTIME to 5 (which should be half a second) and VMIN to 0 (so that the timeout starts immediately), read() just blocks indefinitely if no device is attached.

int32_t OpenDevice(char* device)
{
    if (access(device, R_OK | W_OK))
    {
        LOG(Log_Error, "%s() access(): %s", __func__, strerror(errno));
        goto ERR_ACCESS;
    }

    int32_t fd = open(device, O_RDWR | O_NOCTTY);

    if (fd == -1)
    {
        LOG(Log_Error, "%s() open(): %s", __func__, strerror(errno));
        goto ERR_OPEN;
    }

    struct termios tios;

    if (tcgetattr(fd, &tios))
    {
        LOG(Log_Error, "%s() tcgetattr(): %s", __func__, strerror(errno));
        goto ERR_GETATTR;
    }

    cfmakeraw(&tios);

    if (cfsetspeed(&tios, B115200))
    {
        LOG(Log_Error, "%s() cfsetspeed(): %s", __func__, strerror(errno));
        goto ERR_SETSPEED;
    }

    tios.c_cflag |= CLOCAL | CREAD;
    tios.c_cflag &= ~CRTSCTS;
    tios.c_cc[VMIN] = 0;
    tios.c_cc[VTIME] = 5;

    if (tcsetattr(fd, TCSANOW, &tios))
    {
        LOG(Log_Error, "%s() tcsetattr(): %s", __func__, strerror(errno));
        goto ERR_SETATTR;
    }

    struct termios tios_new;
    tcgetattr(fd, &tios_new);
    if (memcmp(&tios_new, &tios, sizeof(tios)))
    {
        LOG(Log_Error, "%s() failed to set attributes", __func__);
        goto ERR_SETATTR;
    }

    return fd;

ERR_SETATTR:
ERR_SETSPEED:
ERR_GETATTR:
    close(fd);
ERR_OPEN:
ERR_ACCESS:
    return -1;
}

I don't know if it matters, but my application is not running on a PC, but on a Cyclone V SoC (from Altera/Intel) with a Cortex-A9 dual core CPU. The driver used is CONFIG_SERIAL_ALTERA_UART, which creates several /dev/ttyAL devices. The OS itself is a version from Altera's git repository, with the PREEMPT_RT patchset already included (rel_socfpga-4.1.22-ltsi-rt_16.10.02_pr).

PS: I know i could just use select() and call it a day, but i would prefer to keep my code simple instead of adding a bunch of overhead just to get that timeout.

Thanks in advance for any advice on this problem.

sawdust
  • 16,103
  • 3
  • 40
  • 50
Felix G
  • 674
  • 1
  • 7
  • 17
  • to use select() is the right way to do, your code will not be more complex with select(), and the problem is not that one, it is not have a code working or not ;) – bruno Dec 21 '18 at 10:15
  • @bruno Sure, and i know my code would work just fine with select(), but what bugs me is that the read() timeout does not work as described in the tcsetattr() documentation. My code _should_ work, yet it doesn't, and i don't know why. – Felix G Dec 21 '18 at 12:38
  • @FelixG: The place to post this question is the [linux-serial](http://vger.kernel.org/vger-lists.html#linux-serial) mailing list ([archives](https://marc.info/?l=linux-serial&r=1&w=4)), with title `altera-uart: VMIN=0, VTIME>0 read blocks indefinitely if no connection` or similar, with Tobias Klauser CC'd. – Nominal Animal Dec 21 '18 at 20:12
  • @Nominal Animal: thanks for the advice, i will do that. I will of course update this thread with any new information, so it will be easier to find for anyone else facing this problem. – Felix G Dec 23 '18 at 12:37
  • @sawdust: interesting, so i guess it's not the driver's fault then. – Felix G Dec 23 '18 at 12:39
  • @FelixG -- Sorry, but my previous comment was hasty and erroneous. I do get a timeout on x86 as expected. My original test program did not properly check for a read() return code of zero. Since you have not provided a Minimal, Complete, and Verifiable example, the jury is still out whether you have found a regression. (I now think you have not.) – sawdust Dec 24 '18 at 03:17
  • @sawdust: unfortunately i don't have access to the source code since i'm on vacation, so i can't post a more complete example right now. Essentially it was something like size = read( ... ) followed by a check for size < 1. I don't differentiate between a timeout or any other error. However, even if i didn't check the return value at all, at least read() should return after the timeout (which it doesn't) – Felix G Dec 24 '18 at 13:13

1 Answers1

0

read() may not be logical substitute to check if port is connected or not. One solution can be ioctl() on serial port to check modem status argument TIOCMGET.

anand
  • 163
  • 7
  • Sorry, i should have mentioned that the device in question is not a modem, and only the Tx/Rx pins are connected. So, unfortunately, my only option ist to try sending something and wait if i get an answer. – Felix G Dec 23 '18 at 12:31
  • Modem is term used in man page but it is applicable to serial port. Even if it is TX RX pins, kernel driver should take care of giving ioctl support which altera can clarify. Pls refer man page of ioctl() – anand Dec 24 '18 at 01:51