From 7331b884e60673a3ebe2d719d87555d7f6e1e984 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 29 Aug 2025 15:54:35 +0200 Subject: [PATCH 1/3] Added new API for TTYPort - Add `set_vmin_time` method to configure VMIN and VTIME termios parameters - Add `set_non_blocking` method to set VMIN and VTIME to 0 --- src/posix/tty.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index 216c74fb..4f8ce7b4 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -352,6 +352,23 @@ impl TTYPort { .map_err(|e| e.into()) } + /// Set the VMIN and VTIME attributes of the port which determine the behavior + /// of the port on read calls. + /// + /// See [this documentation](http://www.unixwiz.net/techtips/termios-vmin-vtime.html) for + /// more details. + pub fn set_vmin_vtime(&mut self, vmin: u8, vtime: u8) -> Result<()> { + let mut termios = termios::get_termios(self.fd)?; + termios.c_cc[nix::sys::termios::SpecialCharacterIndices::VMIN as usize] = vmin; + termios.c_cc[nix::sys::termios::SpecialCharacterIndices::VTIME as usize] = vtime; + termios::set_termios(self.fd, &termios) + } + + /// Set the TTY port non-blocking by setting VMIN and VTIME to 0. + pub fn set_non_blocking(&mut self) -> Result<()> { + self.set_vmin_vtime(0, 0) + } + /// Attempts to clone the `SerialPort`. This allow you to write and read simultaneously from the /// same serial connection. Please note that if you want a real asynchronous serial port you /// should look at [mio-serial](https://crates.io/crates/mio-serial) or From 0c0b56cb2196f0c51f6aa61d858383dce42fcfc7 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 29 Aug 2025 16:33:51 +0200 Subject: [PATCH 2/3] more generic solution --- src/posix/tty.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index 4f8ce7b4..eef47d8f 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -14,6 +14,37 @@ use crate::{ SerialPortBuilder, StopBits, }; +/// Read mode enumeration. +/// +/// The documentation is strongly based on [this documentation](http://www.unixwiz.net/techtips/termios-vmin-vtime.html). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ReadMode { + /// If data are available in the input queue, it's transferred to the caller's buffer up to a + /// maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks + /// until data arrives, or when VTIME tenths expire from the start of the call. If the timer + /// expires without data, zero is returned. A single byte is sufficient to satisfy this read + /// call, but if more is available in the input queue, it's returned to the caller. + TimedRead { timeout: u8 }, + /// Mapped to VMIN > 0, VTIME = 0 + BlockUntilMinRead { minimal_bytes: u8 }, + /// Read calls are satisfied when either VMIN characters have been transferred to the caller's + /// buffer, or when VTIME tenths expire between characters. Since this timer is not started + /// until the first character arrives, this call can block indefinitely if the serial line is + /// idle. This is the most common mode of operation, and we consider VTIME to be an + /// intercharacter timeout, not an overall one. This call should never return zero bytes read. + Blocking { + /// VTIME value in tenths of a second. + inter_byte_timeout: u8, + minimal_bytes: u8, + }, + /// This is a completely non-blocking read - the call is satisfied immediately directly from + /// the driver's input queue. If data are available, it's transferred to the caller's buffer up + /// to nbytes and returned. Otherwise zero is immediately returned to indicate "no data". + /// This essentially polls the serial port and should be used with care. If done + /// repeatedly, it can consume enormous amounts of processor time and is highly inefficient. + Immediate, +} + /// Convenience method for removing exclusive access from /// a fd and closing it. fn close(fd: RawFd) { @@ -364,9 +395,17 @@ impl TTYPort { termios::set_termios(self.fd, &termios) } - /// Set the TTY port non-blocking by setting VMIN and VTIME to 0. - pub fn set_non_blocking(&mut self) -> Result<()> { - self.set_vmin_vtime(0, 0) + /// Set the TTY port read mode which configures the VMIN and VTIME parameters. + pub fn set_read_mode(&mut self, read_mode: ReadMode) -> Result<()> { + match read_mode { + ReadMode::TimedRead { timeout } => self.set_vmin_vtime(0, timeout), + ReadMode::BlockUntilMinRead { minimal_bytes } => self.set_vmin_vtime(minimal_bytes, 0), + ReadMode::Blocking { + inter_byte_timeout, + minimal_bytes, + } => self.set_vmin_vtime(minimal_bytes, inter_byte_timeout), + ReadMode::Immediate => self.set_vmin_vtime(0, 0), + } } /// Attempts to clone the `SerialPort`. This allow you to write and read simultaneously from the From 01d8248016b4703aa0d88b3786ef03362928a78f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 29 Aug 2025 16:36:14 +0200 Subject: [PATCH 3/3] improve docs --- src/posix/tty.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index eef47d8f..4356d3d7 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -25,7 +25,10 @@ pub enum ReadMode { /// expires without data, zero is returned. A single byte is sufficient to satisfy this read /// call, but if more is available in the input queue, it's returned to the caller. TimedRead { timeout: u8 }, - /// Mapped to VMIN > 0, VTIME = 0 + /// This is a counted read that is satisfied only when at least VMIN characters have been + /// transferred to the caller's buffer - there is no timing component involved. This read can + /// be satisfied from the driver's input queue (where the call could return immediately), or + /// by waiting for new data to arrive: in this respect the call could block indefinitely. BlockUntilMinRead { minimal_bytes: u8 }, /// Read calls are satisfied when either VMIN characters have been transferred to the caller's /// buffer, or when VTIME tenths expire between characters. Since this timer is not started