From 3e5019110729be65e4c8d83e543c456c3786c02d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 6 Apr 2024 09:17:01 +0200 Subject: [PATCH] Add tty line configuration script API On some platforms calling high()/low() to switch line states result in costly system calls whick makes it impossible to swith two or more tty lines simultaneously. To help solve this timing issue we introduce a tty line state configuration API which can be used instead of using high()/low(). Using config_low(line) and config_high(line) one can set up a new line state configuration for multiple lines and then use config_apply() to finally apply the configuration. This will result in only one system call to instruct the serial port drive to switch all the configured line states which should help ensure that the lines are switched simultaneously. Example: script = config_high(DTR); config_low(RTS); config_apply() --- man/tio.1.in | 8 ++++ src/script.c | 104 +++++++++++++++++++----------------------- src/tty.c | 126 +++++++++++++++++++++++++++++++++++++++++++-------- src/tty.h | 6 ++- 4 files changed, 167 insertions(+), 77 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index d36c56a..3757f1b 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -383,6 +383,14 @@ Toggle the tty line. Sleep for seconds. .IP "\fBmsleep(ms)" Sleep for miliseconds. +.IP "\fBconfig_high(line)" +Set tty line state configuration to high. +.IP "\fBconfig_low(line)" +Set tty line state configuration to low. +.IP "\fBapply_config()" +Apply tty line state configuration. Using the line state configuration API +instead of high()/low() will help to make the lines physically switch as +simultaneously as possible. This may solve timing issues on some platforms. .TP 0n Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI diff --git a/src/script.c b/src/script.c index c736a50..c06e93e 100644 --- a/src/script.c +++ b/src/script.c @@ -27,6 +27,7 @@ #include #include #include +#include "misc.h" #include "print.h" #include "options.h" #include "tty.h" @@ -67,60 +68,6 @@ static int msleep(lua_State *L) return 0; } -static void script_line_set(int line, bool value) -{ - switch (line) - { - case TIOCM_DTR: - tty_line_set(serial_fd, "DTR", line, value); - break; - case TIOCM_RTS: - tty_line_set(serial_fd, "RTS", line, value); - break; - case TIOCM_CTS: - tty_line_set(serial_fd, "CTS", line, value); - break; - case TIOCM_DSR: - tty_line_set(serial_fd, "DSR", line, value); - break; - case TIOCM_CD: - tty_line_set(serial_fd, "CD", line, value); - break; - case TIOCM_RI: - tty_line_set(serial_fd, "RI", line, value); - break; - default: - break; - } -} - -static void script_line_toggle(int line) -{ - switch (line) - { - case TIOCM_DTR: - tty_line_toggle(serial_fd, "DTR", line); - break; - case TIOCM_RTS: - tty_line_toggle(serial_fd, "RTS", line); - break; - case TIOCM_CTS: - tty_line_toggle(serial_fd, "CTS", line); - break; - case TIOCM_DSR: - tty_line_toggle(serial_fd, "DSR", line); - break; - case TIOCM_CD: - tty_line_toggle(serial_fd, "CD", line); - break; - case TIOCM_RI: - tty_line_toggle(serial_fd, "RI", line); - break; - default: - break; - } -} - // lua: high(line) static int high(lua_State *L) { @@ -131,7 +78,7 @@ static int high(lua_State *L) return 0; } - script_line_set(line, LINE_HIGH); + tty_line_set(serial_fd, line, LINE_HIGH); return 0; } @@ -146,7 +93,7 @@ static int low(lua_State *L) return 0; } - script_line_set(line, LINE_LOW); + tty_line_set(serial_fd, line, LINE_LOW); return 0; } @@ -161,7 +108,47 @@ static int toggle(lua_State *L) return 0; } - script_line_toggle(line); + tty_line_toggle(serial_fd, line); + + return 0; +} + +// lua: config_high(line) +static int config_high(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + tty_line_config(line, true); + + return 0; +} + +// lua: config_low(line) +static int config_low(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + tty_line_config(line, false); + + return 0; +} + +// lua: config_apply(line) +static int config_apply(lua_State *L) +{ + UNUSED(L); + + tty_line_config_apply(); return 0; } @@ -186,6 +173,9 @@ static const struct luaL_Reg tio_lib[] = { "high", high}, { "low", low}, { "toggle", toggle}, + { "config_high", config_high}, + { "config_low", config_low}, + { "config_apply", config_apply}, {NULL, NULL} }; diff --git a/src/tty.c b/src/tty.c index 38bf1ef..af36ae1 100644 --- a/src/tty.c +++ b/src/tty.c @@ -120,6 +120,13 @@ typedef enum LINE_PULSE } tty_line_mode_t; +typedef struct +{ + int mask; + bool value; + bool reserved; +} tty_line_config_t; + const char random_array[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x20, 0x28, 0x0A, 0x20, @@ -164,6 +171,7 @@ static pthread_t thread; static int pipefd[2]; static pthread_mutex_t mutex_input_ready = PTHREAD_MUTEX_INITIALIZER; static char line[LINE_SIZE_MAX]; +static tty_line_config_t line_config[6] = { }; static void optional_local_echo(char c) { @@ -427,7 +435,89 @@ static void output_hex(char c) } } -void tty_line_set(int fd, const char *name, int mask, bool value) +static const char *tty_line_name(int mask) +{ + switch (mask) + { + case TIOCM_DTR: + return "DTR"; + case TIOCM_RTS: + return "RTS"; + case TIOCM_CTS: + return "CTS"; + case TIOCM_DSR: + return "DSR"; + case TIOCM_CD: + return "CD"; + case TIOCM_RI: + return "RI"; + default: + return NULL; + } +} + +void tty_line_config(int mask, bool value) +{ + int i = 0; + + for (i=0; i<6; i++) + { + if ((line_config[i].mask == mask) || (line_config[i].reserved == false)) + { + line_config[i].mask = mask; + line_config[i].value = value; + line_config[i].reserved = true; + break; + } + } +} + +void tty_line_config_apply(void) +{ + int i = 0; + static int state; + + if (ioctl(fd, TIOCMGET, &state) < 0) + { + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return; + } + + for (i=0; i<6; i++) + { + if (line_config[i].reserved) + { + if (line_config[i].value) + { + // High + state &= ~line_config[i].mask; + tio_printf("Setting %s to HIGH", tty_line_name(line_config[i].mask)); + } + else + { + // Low + state |= line_config[i].mask; + tio_printf("Setting %s to LOW", tty_line_name(line_config[i].mask)); + } + + line_config[i].reserved = true; + } + } + + if (ioctl(fd, TIOCMSET, &state) < 0) + { + tio_warning_printf("Could not set line state configuration (%s)", strerror(errno)); + } + + // Reset configuration + for (i=0; i<6; i++) + { + line_config[i].reserved = false; + line_config[i].mask = -1; + } +} + +void tty_line_set(int fd, int mask, bool value) { int state; @@ -440,12 +530,12 @@ void tty_line_set(int fd, const char *name, int mask, bool value) if (value) { state &= ~mask; - tio_printf("Setting %s to HIGH", name); + tio_printf("Setting %s to HIGH", tty_line_name(mask)); } else { state |= mask; - tio_printf("Setting %s to LOW", name); + tio_printf("Setting %s to LOW", tty_line_name(mask)); } if (ioctl(fd, TIOCMSET, &state) < 0) @@ -454,7 +544,7 @@ void tty_line_set(int fd, const char *name, int mask, bool value) } } -void tty_line_toggle(int fd, const char *line_name, int mask) +void tty_line_toggle(int fd, int mask) { int state; @@ -467,12 +557,12 @@ void tty_line_toggle(int fd, const char *line_name, int mask) if (state & mask) { state &= ~mask; - tio_printf("Setting %s to HIGH", line_name); + tio_printf("Setting %s to HIGH", tty_line_name(mask)); } else { state |= mask; - tio_printf("Setting %s to LOW", line_name); + tio_printf("Setting %s to LOW", tty_line_name(mask)); } if (ioctl(fd, TIOCMSET, &state) < 0) @@ -481,9 +571,9 @@ void tty_line_toggle(int fd, const char *line_name, int mask) } } -static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int duration) +static void tty_line_pulse(int fd, int mask, unsigned int duration) { - tty_line_toggle(fd, line_name, mask); + tty_line_toggle(fd, mask); if (duration > 0) { @@ -491,19 +581,19 @@ static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int delay(duration); } - tty_line_toggle(fd, line_name, mask); + tty_line_toggle(fd, mask); } -static void tty_line_poke(int fd, const char *name, int mask, tty_line_mode_t mode, unsigned int duration) +static void tty_line_poke(int fd, int mask, tty_line_mode_t mode, unsigned int duration) { switch (mode) { case LINE_TOGGLE: - tty_line_toggle(fd, name, mask); + tty_line_toggle(fd, mask); break; case LINE_PULSE: - tty_line_pulse(fd, name, mask, duration); + tty_line_pulse(fd, mask, duration); break; case LINE_OFF: @@ -564,22 +654,22 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) switch (input_char) { case KEY_0: - tty_line_poke(fd, "DTR", TIOCM_DTR, line_mode, option.dtr_pulse_duration); + tty_line_poke(fd, TIOCM_DTR, line_mode, option.dtr_pulse_duration); break; case KEY_1: - tty_line_poke(fd, "RTS", TIOCM_RTS, line_mode, option.rts_pulse_duration); + tty_line_poke(fd, TIOCM_RTS, line_mode, option.rts_pulse_duration); break; case KEY_2: - tty_line_poke(fd, "CTS", TIOCM_CTS, line_mode, option.cts_pulse_duration); + tty_line_poke(fd, TIOCM_CTS, line_mode, option.cts_pulse_duration); break; case KEY_3: - tty_line_poke(fd, "DSR", TIOCM_DSR, line_mode, option.dsr_pulse_duration); + tty_line_poke(fd, TIOCM_DSR, line_mode, option.dsr_pulse_duration); break; case KEY_4: - tty_line_poke(fd, "DCD", TIOCM_CD, line_mode, option.dcd_pulse_duration); + tty_line_poke(fd, TIOCM_CD, line_mode, option.dcd_pulse_duration); break; case KEY_5: - tty_line_poke(fd, "RI", TIOCM_RI, line_mode, option.ri_pulse_duration); + tty_line_poke(fd, TIOCM_RI, line_mode, option.ri_pulse_duration); break; default: tio_warning_printf("Invalid line number"); diff --git a/src/tty.h b/src/tty.h index 4c667ef..eb4b3bb 100644 --- a/src/tty.h +++ b/src/tty.h @@ -39,5 +39,7 @@ void tty_wait_for_device(void); void list_serial_devices(void); void tty_input_thread_create(void); void tty_input_thread_wait_ready(void); -void tty_line_set(int fd, const char *name, int mask, bool value); -void tty_line_toggle(int fd, const char *name, int mask); +void tty_line_set(int fd, int mask, bool value); +void tty_line_toggle(int fd, int mask); +void tty_line_config(int mask, bool value); +void tty_line_config_apply(void);