Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting if a character device has disconnected in Linux in with termios api (c++)

I am using the termios api in Linux to communicate with a serial device. I'm trying to detect if the device has disconnected so I can try to reconnect after some timeout. I have the following example code:

while(1)
{
    FD_ZERO(&rfds);
    FD_SET(tty_fd, &rfds);

    // have tried checking fcntl(tty_fd, F_GETFL); too

    // Blocking call to wait until we have data
    select(tty_fd+1, &rfds, NULL, NULL, NULL);

    // While we have data, collect it
    while (read(tty_fd, &c, 1)>0 && bytesRead++<200)
    {
        serialBuffer.push_back(c);
    }

    bytesRead = 0;

    // Try to parse it
    BufferParse();
}

I'm not actually seeing select() or fcntl return error values (-1) after the ttyUSB device is physically disconnected. I could, of course, check to see if the file in /dev/ exists, but I was hoping there was a more elegant solution.

Would appreciate any advice, thanks!

like image 638
chris12892 Avatar asked Dec 09 '15 03:12

chris12892


1 Answers

First of all it worth mentioning, that the behavior serial-usb is following:

On usb device unplugged disconnect is called

@disconnect: Called when the interface is no longer accessible, usually because its device has been (or is being) disconnected or the driver module is being unloaded.

in our case it is usb_serial_disconnect(struct usb_interface *interface)

which calles usb_serial_console_disconnect(serial), which calles tty_hangup ... and so on.

You can follow chain started from here: http://lxr.free-electrons.com/source/drivers/usb/serial/usb-serial.c#L1091

In short this results in following classic manner:

pselect signals that file descriptor is ready and ioctl(fd, FIONREAD, &len) returns zero len.

That's it you unplugged the device.

Summurizing (derived from your code) :

while(1)
{
    FD_ZERO(&rfds);
    FD_SET(tty_fd, &rfds);

    // have tried checking fcntl(tty_fd, F_GETFL); too

    // Blocking call to wait until we have data
    int ready = select(tty_fd + 1, &rfds, NULL, NULL, NULL);

    if(ready && FD_ISSET(tty_fd, &rfds)) {
      size_t len = 0;
      ioctl(tty_fd, FIONREAD, &len);
      errsv = errno;

      if(len == 0)
      {
         printf("prog_name: zero read from the device: %s.", strerror(errsv));
         /* close fd and cleanup or reconnect etc...*/
         exit(EXIT_FAILURE);
      }

      // While we have data, collect it
      while (read(tty_fd, &c, 1)>0 && bytesRead++<200)
      {
        serialBuffer.push_back(c);
      }

      bytesRead = 0;

      // Try to parse it
      BufferParse();
    }
}

It's a pity that you did not say what kind of device you are using.

In case if your device is capable of RTS/CTS flow control it is also possbile to detect line break.

like image 180
Maquefel Avatar answered Oct 14 '22 05:10

Maquefel