From 896136584a181581b04ef90279a5bfbd2cf50392 Mon Sep 17 00:00:00 2001 From: Daniel Stuart Date: Fri, 24 May 2024 11:08:11 -0300 Subject: [PATCH] Fix hang on TTY read In some cases, when a underlying error happens to a TTY port between poll::wait_read_fd and unistd::read, the read function would hang waiting for some data that is never received. This commit sets the port to non-canonical mode, with VMIN = VTIME = 0. With this change, it has the effect of making reads non-blocking, returning right away. The timeout behaviour is maintained, as prior to reading we call unix::poll through poll::wait_read_fd. Fixes: https://github.com/serialport/serialport-rs/issues/7 --- src/posix/termios.rs | 7 +++++++ src/posix/tty.rs | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/posix/termios.rs b/src/posix/termios.rs index f53af807..b6007300 100644 --- a/src/posix/termios.rs +++ b/src/posix/termios.rs @@ -5,6 +5,7 @@ use crate::{DataBits, FlowControl, Parity, Result, StopBits}; use nix::libc; use std::os::unix::prelude::*; +use std::time::Duration; cfg_if! { if #[cfg(any( @@ -183,6 +184,12 @@ pub(crate) fn set_flow_control(termios: &mut Termios, flow_control: FlowControl) }; } +pub(crate) fn set_timeout(termios: &mut Termios, timeout: Duration) { + let timeout = u128::min(timeout.as_millis() / 100, u8::MAX as u128) as u8; + termios.c_cc[libc::VMIN as usize] = 0; + termios.c_cc[libc::VTIME as usize] = timeout; +} + pub(crate) fn set_data_bits(termios: &mut Termios, data_bits: DataBits) { let size = match data_bits { DataBits::Five => libc::CS5, diff --git a/src/posix/tty.rs b/src/posix/tty.rs index b1d68cff..ba3e3f43 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -134,7 +134,9 @@ impl TTYPort { nix::errno::Errno::result(unsafe { tcgetattr(fd.0, termios.as_mut_ptr()) })?; let mut termios = unsafe { termios.assume_init() }; - // setup TTY for binary serial port access + // Setup TTY for binary serial port access + // Enable non-canonical mode + termios.c_lflag &= !libc::ICANON; // Enable reading from the port and ignore all modem control lines termios.c_cflag |= libc::CREAD | libc::CLOCAL; // Enable raw mode which disables any implicit processing of the input or output data streams @@ -175,6 +177,8 @@ impl TTYPort { termios::set_flow_control(&mut termios, builder.flow_control); termios::set_data_bits(&mut termios, builder.data_bits); termios::set_stop_bits(&mut termios, builder.stop_bits); + // Set termios read to non-blocking, as we handle blocking ourselves (with unix::poll) + termios::set_timeout(&mut termios, Duration::from_millis(0)); #[cfg(not(any(target_os = "ios", target_os = "macos")))] termios::set_baud_rate(&mut termios, builder.baud_rate); #[cfg(any(target_os = "ios", target_os = "macos"))]