From 9703c25503948fe04d8a97a8c0f7bb2f8971f2a9 Mon Sep 17 00:00:00 2001 From: yabu76 Date: Sat, 14 Mar 2026 14:39:50 +0900 Subject: [PATCH] Add a mecanism to prevent unnecessary tcsetattr on changing state Add a mechanism of tty_tcsetattr to skip the mode change and omit the configuration change when the value of the termios structure does not change. In the cygwin environment, tcsetattr() resets DTR/RTS to their default values. Furthermore, add a mechanism of tty_tcsetattr to revert to the state before tcsetattr() was called. However, this method will still cause DTR/RTS spikes. --- src/tty.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/src/tty.c b/src/tty.c index d61584e..b0bdce4 100644 --- a/src/tty.c +++ b/src/tty.c @@ -179,6 +179,7 @@ static struct termios tio, tio_raw, tio_old, stdout_new, stdout_old, stdin_new, unsigned long rx_total = 0, tx_total = 0; static bool connected = false; static bool standard_baudrate = true; +static bool tty_tcsetattr_first = true; static void (*printchar)(char c); static int device_fd; static char hex_chars[2]; @@ -256,28 +257,46 @@ raw_t tty_get_raw_mode(void) } } +/* Activate or change port settings */ int tty_tcsetattr(int fd) { + static struct termios tio_cur; + static int baudrate_cur; + if ( ! connected ) { return -1; } - /* Activate or change port settings */ - int status; + int ret = 0; raw_t raw = tty_get_raw_mode(); - if (raw == RAW_OFF) - { - status = tcsetattr(fd, TCSANOW, &tio); + struct termios *tiop = (raw == RAW_OFF) ? &tio : &tio_raw; + + /* If no need to change, no-op and return */ + if (tty_tcsetattr_first == false && + memcmp(&tio_cur, tiop, sizeof(struct termios)) == 0 && + baudrate_cur == option.baudrate) { + tio_debug_printf("same termios. skip tty_tcsetattr."); + return 0; } - else + +#if defined(__CYGWIN__) + int line_state; + + /* save line state for buggy tcsetattr */ + if (ioctl(fd, TIOCMGET, &line_state) < 0) { - status = tcsetattr(fd, TCSANOW, &tio_raw); + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return -1; } - if (status == -1) +#endif + + if (tcsetattr(fd, TCSANOW, tiop) == -1) { tio_error_printf_silent("Could not apply port settings (%s)", strerror(errno)); - return -1; + tty_tcsetattr_first = true; + ret = -1; + goto tcsetattr_error_end; } /* Set arbitrary baudrate (only works on supported platforms) */ @@ -286,11 +305,55 @@ int tty_tcsetattr(int fd) if (setspeed(device_fd, option.baudrate) != 0) { tio_error_printf_silent("Could not set baudrate speed (%s)", strerror(errno)); - return -1; + tty_tcsetattr_first = true; + ret = -1; + goto setspeed_error_end; } } - return 0; + /* Port settings changed successfully */ + memcpy(&tio_cur, tiop, sizeof(tio_cur)); + baudrate_cur = option.baudrate; + tty_tcsetattr_first = false; + + tcsetattr_error_end: + setspeed_error_end: + +#if defined(__CYGWIN__) + /* restore line state for buggy tcsetattr */ + if (option.flow == FLOW_HARD) + { + /* hardware flow control */ + /* touch DTR only, don't touch RTS */ + int tiocm_dtr = TIOCM_DTR; + int action = (line_state & TIOCM_DTR) ? TIOCMBIS /* DTR=LOW */ : TIOCMBIC /* DTR=HIGH */ ; + + tio_debug_printf("hard flow : tiocm_dtr=%c, action=%c", + (line_state & TIOCM_DTR) ? '1' : '0', (line_state & TIOCM_DTR) ? 'C' : 'S'); + + if (ioctl(fd, action, &tiocm_dtr) < 0) + { + tio_warning_printf("Could not set line state (%s)", strerror(errno)); + ret = -1; + } + } + else + { + /* not hardware flow control */ + /* restore DTR and RTS at the same time */ + + tio_debug_printf("non-hard flow : line_state=0x%x, TIOCM_DTR=0x%x, TIOCM_RTS=0x%x", + line_state, TIOCM_DTR, TIOCM_RTS); + + if (ioctl(fd, TIOCMSET, &line_state) < 0) + { + tio_warning_printf("Could not set line state (%s)", strerror(errno)); + ret = -1; + } + } +#endif + + return ret; } void tty_sync(int fd) @@ -2951,6 +3014,7 @@ int tty_connect(void) } /* Activate new port settings and set speed for non-interactive phase */ + tty_tcsetattr_first = true; status = tty_tcsetattr(device_fd); if (status == -1) {