Reading a serial port with a timeout on Linux

Howdy. I'm having difficulty opening a serial port with Rust on Linux where I'm also attempting to specify a timeout on reads (VTIME). My reads block indefinitely whereas I'm expecting them to return after one sceond.

I realise I could use the serialport-rs project, but it is bothering me that I can't read with a timeout in a similar fashion to how I'd go about it in C. Admittedly, it could be non-Rust related, but I'm trying to follow the giudelines on this. Here's my code for establishing the serial port connection:

    let fd = OpenOptions::new()
        .read(true)
        .write(true)
        .custom_flags(libc::O_NOCTTY | libc::O_NDELAY) // UPDATE: Makes no diff if O_NDELAY is specified
        .open(uart_path)?;

    let r = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFL, 0) };
    if r < 0 {
        return Err(io::Error::last_os_error().into());
    }

    let mut tty = termios::tcgetattr(fd.as_raw_fd()).unwrap();

    termios::cfmakeraw(&mut tty);

    termios::cfsetispeed(&mut tty, BaudRate::B115200).unwrap();
    termios::cfsetospeed(&mut tty, BaudRate::B115200).unwrap();

    tty.control_flags.set(ControlFlags::CREAD, true);
    tty.control_flags.set(ControlFlags::CLOCAL, true);
    tty.control_flags.set(ControlFlags::CSTOPB, false);
    tty.control_flags.set(ControlFlags::CRTSCTS, false);

    tty.control_chars[libc::VMIN] = MIN_PACKET_SIZE as u8;
    tty.control_chars[libc::VTIME] = 10;

    termios::tcsetattr(fd.as_raw_fd(), SetArg::TCSANOW, &tty).unwrap();

Thanks for any guidance.

It says explicitly in the guide link you've given:

Timeouts are ignored in canonical input mode or when the NDELAY option is set on the file via open or fcntl

You are setting O_NDELAY, hence the timeout is being ignored.


Sidenote: You seem to be using an older version of termios than the latest available on crates.io.

Ah soz - meant to also say that it makes no diff to my code whether O_NDELAY is applied or not.

Here's what I'm using... there's something later?

nix = { version = "0.25.0", features = ["ioctl", "term"] }

How about this warning from the same guide?

This method allows you to tell the serial driver you need exactly N bytes and any read call will return 0 or N bytes. However, the timeout only applies to the first character read, so if for some reason the driver misses one character inside the N byte packet then the read call could block forever waiting for additional input characters.

Nah it's okay, I thought you were using the termios crate.

I don't think that is it either... At present, the other end isn't sending anything back at all as I'm just wanting to see the read call timeout.

Hmm. As a sanity check, does the C version of this code exhibit the timeout you expect?

I've not actually coded this up in C; I'm trying to take the same approach as if I was using C though. I could well be doing something very wrong.

I'm actually now beginning to think that the problem is with my target being mipsel-unknown-linux-musl. There's an old issue with nix-rust that makes me suspicious, plus no explicit Tier 1 support from the project's README.

I also notice that the Baud enum is being cast to a u32, which is bad because B115200 won't have the right value. I might try the rustix crate as it is lists support for my target, or perhaps just use serialport-rs as I'm now appreciating why. :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.