Skip to content

Commit

Permalink
Fix hang on TTY read
Browse files Browse the repository at this point in the history
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: serialport#7
  • Loading branch information
danielstuart14 committed May 24, 2024
1 parent 0a75184 commit 8961365
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 1 deletion.
7 changes: 7 additions & 0 deletions src/posix/termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion src/posix/tty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"))]
Expand Down

0 comments on commit 8961365

Please sign in to comment.