From 0becfa3274c80cb8d18fed0325c776eb4bfdb1ad Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 1 Apr 2024 15:37:25 +0200 Subject: [PATCH] Add Lua scripting feature Add support for running Lua scripts that can manipulate the tty control lines. Script is activated automatically on connect or manually via in session key command. The Lua scripting feature opens up for many posibilities in the future such as adding expect like functionality to easily and programatically interact with the connected device. --- example/config | 8 + example/control-lines-test.lua | 7 + man/tio.1.in | 59 ++++++- src/bash-completion/tio.in | 15 ++ src/configfile.c | 17 ++ src/meson.build | 17 +- src/options.c | 46 ++++++ src/options.h | 5 + src/script.c | 280 +++++++++++++++++++++++++++++++++ src/script.h | 32 ++++ src/tty.c | 171 ++++++++++++-------- src/tty.h | 5 + 12 files changed, 590 insertions(+), 72 deletions(-) create mode 100644 example/control-lines-test.lua create mode 100644 src/script.c create mode 100644 src/script.h diff --git a/example/config b/example/config index c208fba..2c01505 100644 --- a/example/config +++ b/example/config @@ -29,6 +29,7 @@ color = bold rs-485 = disable response-wait = disable alert = none +script-run = always # Sub-configurations @@ -63,3 +64,10 @@ device = /dev/ttyUSB0 rs-485 = enable rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX color = 13 + +[esp32] +device = /dev/ttyUSB0 +color = 14 +script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) +script-run = always + diff --git a/example/control-lines-test.lua b/example/control-lines-test.lua new file mode 100644 index 0000000..9f0d493 --- /dev/null +++ b/example/control-lines-test.lua @@ -0,0 +1,7 @@ +high(DTR) +low(RTS) +msleep(100) +low(DTR) +high(RTS) +msleep(100) +low(RTS) diff --git a/man/tio.1.in b/man/tio.1.in index 97ef873..41138de 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -103,7 +103,7 @@ Enable local echo. Enable line timestamp. .TP -.BR " \-\-timestamp-format \fI +.BR " \-\-timestamp\-format \fI Set timestamp format to any of the following timestamp formats: .RS @@ -137,12 +137,12 @@ tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. The filename can be manually set using the \-\-log-file option. .TP -.BR " \-\-log-file \fI +.BR " \-\-log\-file \fI Set log filename. .TP -.BR " \-\-log-append +.BR " \-\-log\-append Append to log file. @@ -228,7 +228,7 @@ At present there is a hardcoded limit of 16 clients connected at one time. .RE .TP -.BR \-r ", " \-\-response-wait +.BR \-r ", " \-\-response\-wait Wait for line response then quit. A line is considered any string terminated with a NL character. If no line is received tio will quit after response @@ -279,6 +279,23 @@ will sound the bell twice or blink twice on disconnect. Default value is "none". +.TP +.BR "\-\-script \fI + +Run script from string. + +.TP +.BR "\-\-script\-file \fI + +Run script from file with filename. + +.TP +.BR "\-\-script\-run once|always|never" + +Run script on connect once, always, or never. + +Default value is "always". + .TP .BR \-v ", " \-\-version @@ -317,6 +334,8 @@ Toggle MSB to LSB bit order Pulse serial port line .IP "\fBctrl-t q" Quit +.IP "\fBctrl-t r" +Run script .IP "\fBctrl-t s" Show TX/RX statistics .IP "\fBctrl-t t" @@ -341,6 +360,26 @@ Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR. +.SH "SCRIPT API" +.PP +Tio suppots Lua scripting for manipulating the tty device. In addition to the +Lua API tio makes the following functions available: + +.TP 6n +.IP "\fBhigh(line)" +Set tty line high. +.IP "\fBlow(line)" +Set tty line low. +.IP "\fBtoggle(line)" +Toggle the tty line. +.IP "\fBsleep(seconds)" +Sleep for seconds. +.IP "\fBmsleep(ms)" +Sleep for miliseconds. + +.TP 0n +Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI + .SH "CONFIGURATION FILE" .PP Options can be set via configuration file using the INI format. \fBtio\fR uses @@ -428,6 +467,12 @@ Enable RS-485 mode Set RS-485 configuration .IP "\fBalert" Set alert action on connect/disconnect +.IP "\fBscript" +Run script from string +.IP "\fBscript-file" +Run script from file +.IP "\fBscript-run" +Run script on connect. .SH "CONFIGURATION FILE EXAMPLES" @@ -582,6 +627,12 @@ Enable RS-485 mode: $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 +.TP +Script manipulation of DTR and RTS lines upon first connect: + +$ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 + + .SH "WEBSITE" .PP Visit https://tio.github.io diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 908f7a3..6466db1 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -37,6 +37,9 @@ _tio() --rs-485-config \ --alert \ --mute \ + --script \ + --script-file \ + --script-run \ -v --version \ -h --help" @@ -151,6 +154,18 @@ _tio() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + --script) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + --script-file) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + --script-run) + COMPREPLY=( $(compgen -W "once always never" -- ${cur}) ) + return 0 + ;; -v | --version) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index ef97f31..2e50e05 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -60,6 +60,9 @@ struct config_t char *log_filename; char *socket; char *map; + char *script; + char *script_filename; + bool script_run; }; static struct config_t c; @@ -297,6 +300,20 @@ static int data_handler(void *user, const char *section, const char *name, { // Do nothing } + else if (!strcmp(name, "script")) + { + asprintf(&c.script, "%s", value); + option.script = c.script; + } + else if (!strcmp(name, "script-file")) + { + asprintf(&c.script_filename, "%s", value); + option.script_filename = c.script_filename; + } + else if (!strcmp(name, "script-run")) + { + option.script_run = script_run_option_parse(value); + } else { tio_warning_printf("Unknown option '%s' in configuration file, ignored", name); diff --git a/src/meson.build b/src/meson.build index 2b4a0fe..dddddd5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,14 +18,27 @@ tio_sources = [ 'rs485.c', 'timestamp.c', 'alert.c', - 'xymodem.c' + 'xymodem.c', + 'script.c' ] + +foreach name: ['lua-5.4', 'lua-5.3', 'lua-5.2', 'lua-5.1', 'lua'] + lua_dep = dependency(name, version: '>=5.1', required: false) + if lua_dep.found() + break + endif +endforeach +if not lua_dep.found() + error('Lua could not be found!') +endif + tio_dep = [ dependency('threads', required: true), dependency('inih', required: true, fallback : ['libinih', 'inih_dep'], - default_options: ['default_library=static', 'distro_install=false']) + default_options: ['default_library=static', 'distro_install=false']), + lua_dep ] tio_c_args = ['-Wno-unused-result'] diff --git a/src/options.c b/src/options.c index 88e2379..1f70122 100644 --- a/src/options.c +++ b/src/options.c @@ -39,6 +39,7 @@ #include "timestamp.h" #include "alert.h" #include "log.h" +#include "script.h" enum opt_t { @@ -54,6 +55,9 @@ enum opt_t OPT_ALERT, OPT_COMPLETE_SUB_CONFIGS, OPT_MUTE, + OPT_SCRIPT, + OPT_SCRIPT_FILE, + OPT_SCRIPT_RUN, }; /* Default options */ @@ -96,6 +100,9 @@ struct option_t option = .rs485_delay_rts_after_send = -1, .alert = ALERT_NONE, .complete_sub_configs = false, + .script = NULL, + .script_filename = NULL, + .script_run = SCRIPT_RUN_ALWAYS, }; void print_help(char *argv[]) @@ -134,6 +141,9 @@ void print_help(char *argv[]) printf(" --rs-485-config Set RS-485 configuration\n"); printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n"); printf(" --mute Mute tio\n"); + printf(" --script Run script from string\n"); + printf(" --script-file Run script from file\n"); + printf(" --script-run once|always|never Run script on connect (default: always)\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -202,6 +212,27 @@ void line_pulse_duration_option_parse(const char *arg) free(buffer); } +enum script_run_t script_run_option_parse(const char *arg) +{ + if (strcmp("once", arg) == 0) + { + return SCRIPT_RUN_ONCE; + } + else if (strcmp("always", arg) == 0) + { + return SCRIPT_RUN_ALWAYS; + } + else if (strcmp("never", arg) == 0) + { + return SCRIPT_RUN_NEVER; + } + else + { + tio_error_printf("Invalid script run option"); + exit(EXIT_FAILURE); + } +} + void options_print() { tio_printf(" Device: %s", option.tty_device); @@ -276,6 +307,9 @@ void options_parse(int argc, char *argv[]) {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, {"alert", required_argument, 0, OPT_ALERT }, {"mute", no_argument, 0, OPT_MUTE }, + {"script", required_argument, 0, OPT_SCRIPT }, + {"script-file", required_argument, 0, OPT_SCRIPT_FILE }, + {"script-run", required_argument, 0, OPT_SCRIPT_RUN }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS}, @@ -439,6 +473,18 @@ void options_parse(int argc, char *argv[]) option.mute = true; break; + case OPT_SCRIPT: + option.script = optarg; + break; + + case OPT_SCRIPT_FILE: + option.script_filename = optarg; + break; + + case OPT_SCRIPT_RUN: + option.script_run = script_run_option_parse(optarg); + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index f1f2f73..e909b98 100644 --- a/src/options.h +++ b/src/options.h @@ -26,6 +26,7 @@ #include #include #include +#include "script.h" #include "timestamp.h" #include "alert.h" @@ -69,6 +70,9 @@ struct option_t int32_t rs485_delay_rts_after_send; enum alert_t alert; bool complete_sub_configs; + const char *script; + const char *script_filename; + enum script_run_t script_run; }; extern struct option_t option; @@ -78,3 +82,4 @@ void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); void line_pulse_duration_option_parse(const char *arg); +enum script_run_t script_run_option_parse(const char *arg); diff --git a/src/script.c b/src/script.c new file mode 100644 index 0000000..c736a50 --- /dev/null +++ b/src/script.c @@ -0,0 +1,280 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2024 Martin Lund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "print.h" +#include "options.h" +#include "tty.h" + +static int serial_fd; + +// lua: sleep(seconds) +static int sleep_(lua_State *L) +{ + long seconds = lua_tointeger(L, 1); + + if (seconds < 0) + { + return 0; + } + + tio_printf("Sleeping %ld seconds", seconds); + + sleep(seconds); + + return 0; +} + +// lua: msleep(miliseconds) +static int msleep(lua_State *L) +{ + long mseconds = lua_tointeger(L, 1); + long useconds = mseconds * 1000; + + if (useconds < 0) + { + return 0; + } + + tio_printf("Sleeping %ld ms", mseconds); + usleep(useconds); + + 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) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + script_line_set(line, LINE_HIGH); + + return 0; +} + +// lua: low(line) +static int low(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + script_line_set(line, LINE_LOW); + + return 0; +} + +// lua: toggle(line) +static int toggle(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + script_line_toggle(line); + + return 0; +} + +static void script_buffer_run(lua_State *L, const char *script_buffer) +{ + int error; + + error = luaL_loadbuffer(L, script_buffer, strlen(script_buffer), "tio") || + lua_pcall(L, 0, 0, 0); + if (error) + { + tio_warning_printf("%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); /* pop error message from the stack */ + } +} + +static const struct luaL_Reg tio_lib[] = +{ + { "sleep", sleep_}, + { "msleep", msleep}, + { "high", high}, + { "low", low}, + { "toggle", toggle}, + {NULL, NULL} +}; + +#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 +/* +** Adapted from Lua 5.2.0 (for backwards compatibility) +*/ +static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) +{ + luaL_checkstack(L, nup+1, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + lua_pushstring(L, l->name); + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -(nup+1)); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); + } + lua_pop(L, nup); /* remove upvalues */ +} +#endif + +int lua_register_tio(lua_State *L) +{ + // Register lxi functions + lua_getglobal(L, "_G"); + luaL_setfuncs(L, tio_lib, 0); + lua_pop(L, 1); + + return 0; +} + +void script_file_run(lua_State *L, const char *filename) +{ + if (strlen(filename) == 0) + { + tio_warning_printf("Missing script filename\n"); + return; + } + + if (luaL_dofile(L, filename)) + { + tio_warning_printf("%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); /* pop error message from the stack */ + return; + } +} + +void script_set_global(lua_State *L, const char *name, long value) +{ + lua_pushnumber(L, value); + lua_setglobal(L, name); +} + +void script_set_globals(lua_State *L) +{ + script_set_global(L, "DTR", TIOCM_DTR); + script_set_global(L, "RTS", TIOCM_RTS); + script_set_global(L, "CTS", TIOCM_CTS); + script_set_global(L, "DSR", TIOCM_DSR); + script_set_global(L, "CD", TIOCM_CD); + script_set_global(L, "RI", TIOCM_RI); +} + +void script_run(int fd) +{ + lua_State *L; + + serial_fd = fd; + + L = luaL_newstate(); + luaL_openlibs(L); + + // Bind tio functions + lua_register_tio(L); + + // Initialize globals + script_set_globals(L); + + if (option.script_filename != NULL) + { + tio_printf("Running script %s", option.script_filename); + script_file_run(L, option.script_filename); + } + else if (option.script != NULL) + { + tio_printf("Running script"); + script_buffer_run(L, option.script); + } + + lua_close(L); +} diff --git a/src/script.h b/src/script.h new file mode 100644 index 0000000..0ea0faf --- /dev/null +++ b/src/script.h @@ -0,0 +1,32 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2024 Martin Lund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#pragma once + +enum script_run_t +{ + SCRIPT_RUN_ONCE, + SCRIPT_RUN_ALWAYS, + SCRIPT_RUN_NEVER, + SCRIPT_RUN_END, +}; + +void script_run(int fd); diff --git a/src/tty.c b/src/tty.c index e8ba150..6921d04 100644 --- a/src/tty.c +++ b/src/tty.c @@ -55,6 +55,7 @@ #include "alert.h" #include "timestamp.h" #include "misc.h" +#include "script.h" /* tty device listing configuration */ @@ -103,6 +104,7 @@ #define KEY_M 0x6D #define KEY_P 0x70 #define KEY_Q 0x71 +#define KEY_R 0x72 #define KEY_S 0x73 #define KEY_T 0x74 #define KEY_U 0x55 @@ -111,12 +113,12 @@ #define KEY_Y 0x79 #define KEY_Z 0x7a -enum line_mode_t +typedef enum { LINE_OFF, LINE_TOGGLE, LINE_PULSE -}; +} tty_line_mode_t; const char random_array[] = { @@ -424,67 +426,87 @@ static void output_hex(char c) } } -static void toggle_line(const char *line_name, int mask, enum line_mode_t line_mode) +void tty_line_set(int fd, const char *name, int mask, bool value) { int state; - if (line_mode == LINE_TOGGLE) + if (ioctl(fd, TIOCMGET, &state) < 0) { - // Toggle line - if (ioctl(fd, TIOCMGET, &state) < 0) - { - tio_warning_printf("Could not get line state (%s)", strerror(errno)); - } - else - { - if (state & mask) - { - state &= ~mask; - tio_printf("Setting %s to HIGH", line_name); - } - else - { - state |= mask; - tio_printf("Setting %s to LOW", line_name); - } - if (ioctl(fd, TIOCMSET, &state) < 0) - tio_warning_printf("Could not set line state (%s)", strerror(errno)); - } - } else if (line_mode == LINE_PULSE) + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return; + } + + if (value) { - int duration = 0; - // Pulse line - toggle_line(line_name, mask, LINE_TOGGLE); - switch (mask) - { - case TIOCM_DTR: - duration = option.dtr_pulse_duration; - break; - case TIOCM_RTS: - duration = option.rts_pulse_duration; - break; - case TIOCM_CTS: - duration = option.cts_pulse_duration; - break; - case TIOCM_DSR: - duration = option.dsr_pulse_duration; - break; - case TIOCM_CD: - duration = option.dcd_pulse_duration; - break; - case TIOCM_RI: - duration = option.ri_pulse_duration; - break; - default: - duration = 0; - break; - } - if (duration > 0) - { - tio_printf("Waiting %d ms", duration); - delay(duration); - } - toggle_line(line_name, mask, LINE_TOGGLE); + state &= ~mask; + tio_printf("Setting %s to HIGH", name); + } + else + { + state |= mask; + tio_printf("Setting %s to LOW", name); + } + + if (ioctl(fd, TIOCMSET, &state) < 0) + { + tio_warning_printf("Could not set line state (%s)", strerror(errno)); + } +} + +void tty_line_toggle(int fd, const char *line_name, int mask) +{ + int state; + + if (ioctl(fd, TIOCMGET, &state) < 0) + { + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return; + } + + if (state & mask) + { + state &= ~mask; + tio_printf("Setting %s to HIGH", line_name); + } + else + { + state |= mask; + tio_printf("Setting %s to LOW", line_name); + } + + if (ioctl(fd, TIOCMSET, &state) < 0) + { + tio_warning_printf("Could not set line state (%s)", strerror(errno)); + } +} + +static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int duration) +{ + tty_line_toggle(fd, line_name, mask); + + if (duration > 0) + { + tio_printf("Waiting %d ms", duration); + delay(duration); + } + + tty_line_toggle(fd, line_name, mask); +} + +static void tty_line_poke(int fd, const char *name, int mask, tty_line_mode_t mode, unsigned int duration) +{ + switch (mode) + { + case LINE_TOGGLE: + tty_line_toggle(fd, name, mask); + break; + + case LINE_PULSE: + tty_line_pulse(fd, name, mask, duration); + break; + + case LINE_OFF: + break; } } @@ -520,7 +542,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) char unused_char; bool unused_bool; int state; - static enum line_mode_t line_mode = LINE_OFF; + static tty_line_mode_t line_mode = LINE_OFF; static char previous_char = 0; /* Ignore unused arguments */ @@ -534,29 +556,29 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) forward = &unused_bool; } + // Handle tty line toggle and pulse action if (line_mode) { - // Handle line toggle number action *forward = false; switch (input_char) { case KEY_0: - toggle_line("DTR", TIOCM_DTR, line_mode); + tty_line_poke(fd, "DTR", TIOCM_DTR, line_mode, option.dtr_pulse_duration); break; case KEY_1: - toggle_line("RTS", TIOCM_RTS, line_mode); + tty_line_poke(fd, "RTS", TIOCM_RTS, line_mode, option.rts_pulse_duration); break; case KEY_2: - toggle_line("CTS", TIOCM_CTS, line_mode); + tty_line_poke(fd, "CTS", TIOCM_CTS, line_mode, option.cts_pulse_duration); break; case KEY_3: - toggle_line("DSR", TIOCM_DSR, line_mode); + tty_line_poke(fd, "DSR", TIOCM_DSR, line_mode, option.dsr_pulse_duration); break; case KEY_4: - toggle_line("DCD", TIOCM_CD, line_mode); + tty_line_poke(fd, "DCD", TIOCM_CD, line_mode, option.dcd_pulse_duration); break; case KEY_5: - toggle_line("RI", TIOCM_RI, line_mode); + tty_line_poke(fd, "RI", TIOCM_RI, line_mode, option.ri_pulse_duration); break; default: tio_warning_printf("Invalid line number"); @@ -601,6 +623,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c m Toggle MSB to LSB bit order", option.prefix_key); tio_printf(" ctrl-%c p Pulse serial port line", option.prefix_key); tio_printf(" ctrl-%c q Quit", option.prefix_key); + tio_printf(" ctrl-%c r Run script", option.prefix_key); tio_printf(" ctrl-%c s Show statistics", option.prefix_key); tio_printf(" ctrl-%c t Toggle line timestamp mode", option.prefix_key); tio_printf(" ctrl-%c U Toggle conversion to uppercase on output", option.prefix_key); @@ -726,6 +749,11 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) /* Exit upon ctrl-t q sequence */ exit(EXIT_SUCCESS); + case KEY_R: + /* Run script */ + script_run(fd); + break; + case KEY_S: /* Show tx/rx statistics upon ctrl-t s sequence */ tio_printf("Statistics:"); @@ -1383,6 +1411,17 @@ int tty_connect(void) } } + /* Manage script activation */ + if (option.script_run != SCRIPT_RUN_NEVER) + { + script_run(fd); + + if (option.script_run == SCRIPT_RUN_ONCE) + { + option.script_run = SCRIPT_RUN_NEVER; + } + } + /* Input loop */ while (true) { diff --git a/src/tty.h b/src/tty.h index add8bf1..4c667ef 100644 --- a/src/tty.h +++ b/src/tty.h @@ -23,6 +23,9 @@ #include +#define LINE_HIGH true +#define LINE_LOW false + extern bool interactive_mode; extern bool map_i_nl_cr; extern bool map_i_cr_nl; @@ -36,3 +39,5 @@ 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);