mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
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.
This commit is contained in:
parent
6fee8514f4
commit
0becfa3274
12 changed files with 590 additions and 72 deletions
|
|
@ -29,6 +29,7 @@ color = bold
|
||||||
rs-485 = disable
|
rs-485 = disable
|
||||||
response-wait = disable
|
response-wait = disable
|
||||||
alert = none
|
alert = none
|
||||||
|
script-run = always
|
||||||
|
|
||||||
# Sub-configurations
|
# Sub-configurations
|
||||||
|
|
||||||
|
|
@ -63,3 +64,10 @@ device = /dev/ttyUSB0
|
||||||
rs-485 = enable
|
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
|
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
|
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
|
||||||
|
|
||||||
|
|
|
||||||
7
example/control-lines-test.lua
Normal file
7
example/control-lines-test.lua
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
high(DTR)
|
||||||
|
low(RTS)
|
||||||
|
msleep(100)
|
||||||
|
low(DTR)
|
||||||
|
high(RTS)
|
||||||
|
msleep(100)
|
||||||
|
low(RTS)
|
||||||
59
man/tio.1.in
59
man/tio.1.in
|
|
@ -103,7 +103,7 @@ Enable local echo.
|
||||||
Enable line timestamp.
|
Enable line timestamp.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR " \-\-timestamp-format \fI<format>
|
.BR " \-\-timestamp\-format \fI<format>
|
||||||
|
|
||||||
Set timestamp format to any of the following timestamp formats:
|
Set timestamp format to any of the following timestamp formats:
|
||||||
.RS
|
.RS
|
||||||
|
|
@ -137,12 +137,12 @@ tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log.
|
||||||
The filename can be manually set using the \-\-log-file option.
|
The filename can be manually set using the \-\-log-file option.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR " \-\-log-file \fI<filename>
|
.BR " \-\-log\-file \fI<filename>
|
||||||
|
|
||||||
Set log filename.
|
Set log filename.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR " \-\-log-append
|
.BR " \-\-log\-append
|
||||||
|
|
||||||
Append to log file.
|
Append to log file.
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ At present there is a hardcoded limit of 16 clients connected at one time.
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR \-r ", " \-\-response-wait
|
.BR \-r ", " \-\-response\-wait
|
||||||
|
|
||||||
Wait for line response then quit. A line is considered any string terminated
|
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
|
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".
|
Default value is "none".
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-script \fI<string>
|
||||||
|
|
||||||
|
Run script from string.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-script\-file \fI<filename>
|
||||||
|
|
||||||
|
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
|
.TP
|
||||||
.BR \-v ", " \-\-version
|
.BR \-v ", " \-\-version
|
||||||
|
|
||||||
|
|
@ -317,6 +334,8 @@ Toggle MSB to LSB bit order
|
||||||
Pulse serial port line
|
Pulse serial port line
|
||||||
.IP "\fBctrl-t q"
|
.IP "\fBctrl-t q"
|
||||||
Quit
|
Quit
|
||||||
|
.IP "\fBctrl-t r"
|
||||||
|
Run script
|
||||||
.IP "\fBctrl-t s"
|
.IP "\fBctrl-t s"
|
||||||
Show TX/RX statistics
|
Show TX/RX statistics
|
||||||
.IP "\fBctrl-t t"
|
.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
|
representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or
|
||||||
\fI0A\fR.
|
\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"
|
.SH "CONFIGURATION FILE"
|
||||||
.PP
|
.PP
|
||||||
Options can be set via configuration file using the INI format. \fBtio\fR uses
|
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
|
Set RS-485 configuration
|
||||||
.IP "\fBalert"
|
.IP "\fBalert"
|
||||||
Set alert action on connect/disconnect
|
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"
|
.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
|
$ 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"
|
.SH "WEBSITE"
|
||||||
.PP
|
.PP
|
||||||
Visit https://tio.github.io
|
Visit https://tio.github.io
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ _tio()
|
||||||
--rs-485-config \
|
--rs-485-config \
|
||||||
--alert \
|
--alert \
|
||||||
--mute \
|
--mute \
|
||||||
|
--script \
|
||||||
|
--script-file \
|
||||||
|
--script-run \
|
||||||
-v --version \
|
-v --version \
|
||||||
-h --help"
|
-h --help"
|
||||||
|
|
||||||
|
|
@ -151,6 +154,18 @@ _tio()
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
return 0
|
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)
|
-v | --version)
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ struct config_t
|
||||||
char *log_filename;
|
char *log_filename;
|
||||||
char *socket;
|
char *socket;
|
||||||
char *map;
|
char *map;
|
||||||
|
char *script;
|
||||||
|
char *script_filename;
|
||||||
|
bool script_run;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct config_t c;
|
static struct config_t c;
|
||||||
|
|
@ -297,6 +300,20 @@ static int data_handler(void *user, const char *section, const char *name,
|
||||||
{
|
{
|
||||||
// Do nothing
|
// 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
|
else
|
||||||
{
|
{
|
||||||
tio_warning_printf("Unknown option '%s' in configuration file, ignored", name);
|
tio_warning_printf("Unknown option '%s' in configuration file, ignored", name);
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,27 @@ tio_sources = [
|
||||||
'rs485.c',
|
'rs485.c',
|
||||||
'timestamp.c',
|
'timestamp.c',
|
||||||
'alert.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 = [
|
tio_dep = [
|
||||||
dependency('threads', required: true),
|
dependency('threads', required: true),
|
||||||
dependency('inih', required: true,
|
dependency('inih', required: true,
|
||||||
fallback : ['libinih', 'inih_dep'],
|
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']
|
tio_c_args = ['-Wno-unused-result']
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
#include "timestamp.h"
|
#include "timestamp.h"
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "script.h"
|
||||||
|
|
||||||
enum opt_t
|
enum opt_t
|
||||||
{
|
{
|
||||||
|
|
@ -54,6 +55,9 @@ enum opt_t
|
||||||
OPT_ALERT,
|
OPT_ALERT,
|
||||||
OPT_COMPLETE_SUB_CONFIGS,
|
OPT_COMPLETE_SUB_CONFIGS,
|
||||||
OPT_MUTE,
|
OPT_MUTE,
|
||||||
|
OPT_SCRIPT,
|
||||||
|
OPT_SCRIPT_FILE,
|
||||||
|
OPT_SCRIPT_RUN,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Default options */
|
/* Default options */
|
||||||
|
|
@ -96,6 +100,9 @@ struct option_t option =
|
||||||
.rs485_delay_rts_after_send = -1,
|
.rs485_delay_rts_after_send = -1,
|
||||||
.alert = ALERT_NONE,
|
.alert = ALERT_NONE,
|
||||||
.complete_sub_configs = false,
|
.complete_sub_configs = false,
|
||||||
|
.script = NULL,
|
||||||
|
.script_filename = NULL,
|
||||||
|
.script_run = SCRIPT_RUN_ALWAYS,
|
||||||
};
|
};
|
||||||
|
|
||||||
void print_help(char *argv[])
|
void print_help(char *argv[])
|
||||||
|
|
@ -134,6 +141,9 @@ void print_help(char *argv[])
|
||||||
printf(" --rs-485-config <config> Set RS-485 configuration\n");
|
printf(" --rs-485-config <config> Set RS-485 configuration\n");
|
||||||
printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n");
|
printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n");
|
||||||
printf(" --mute Mute tio\n");
|
printf(" --mute Mute tio\n");
|
||||||
|
printf(" --script <string> Run script from string\n");
|
||||||
|
printf(" --script-file <filename> 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(" -v, --version Display version\n");
|
||||||
printf(" -h, --help Display help\n");
|
printf(" -h, --help Display help\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
@ -202,6 +212,27 @@ void line_pulse_duration_option_parse(const char *arg)
|
||||||
free(buffer);
|
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()
|
void options_print()
|
||||||
{
|
{
|
||||||
tio_printf(" Device: %s", option.tty_device);
|
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 },
|
{"rs-485-config", required_argument, 0, OPT_RS485_CONFIG },
|
||||||
{"alert", required_argument, 0, OPT_ALERT },
|
{"alert", required_argument, 0, OPT_ALERT },
|
||||||
{"mute", no_argument, 0, OPT_MUTE },
|
{"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' },
|
{"version", no_argument, 0, 'v' },
|
||||||
{"help", no_argument, 0, 'h' },
|
{"help", no_argument, 0, 'h' },
|
||||||
{"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS},
|
{"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS},
|
||||||
|
|
@ -439,6 +473,18 @@ void options_parse(int argc, char *argv[])
|
||||||
option.mute = true;
|
option.mute = true;
|
||||||
break;
|
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':
|
case 'v':
|
||||||
printf("tio v%s\n", VERSION);
|
printf("tio v%s\n", VERSION);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include "script.h"
|
||||||
#include "timestamp.h"
|
#include "timestamp.h"
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
|
|
||||||
|
|
@ -69,6 +70,9 @@ struct option_t
|
||||||
int32_t rs485_delay_rts_after_send;
|
int32_t rs485_delay_rts_after_send;
|
||||||
enum alert_t alert;
|
enum alert_t alert;
|
||||||
bool complete_sub_configs;
|
bool complete_sub_configs;
|
||||||
|
const char *script;
|
||||||
|
const char *script_filename;
|
||||||
|
enum script_run_t script_run;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct option_t option;
|
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 options_parse_final(int argc, char *argv[]);
|
||||||
|
|
||||||
void line_pulse_duration_option_parse(const char *arg);
|
void line_pulse_duration_option_parse(const char *arg);
|
||||||
|
enum script_run_t script_run_option_parse(const char *arg);
|
||||||
|
|
|
||||||
280
src/script.c
Normal file
280
src/script.c
Normal file
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
32
src/script.h
Normal file
32
src/script.h
Normal file
|
|
@ -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);
|
||||||
125
src/tty.c
125
src/tty.c
|
|
@ -55,6 +55,7 @@
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
#include "timestamp.h"
|
#include "timestamp.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "script.h"
|
||||||
|
|
||||||
/* tty device listing configuration */
|
/* tty device listing configuration */
|
||||||
|
|
||||||
|
|
@ -103,6 +104,7 @@
|
||||||
#define KEY_M 0x6D
|
#define KEY_M 0x6D
|
||||||
#define KEY_P 0x70
|
#define KEY_P 0x70
|
||||||
#define KEY_Q 0x71
|
#define KEY_Q 0x71
|
||||||
|
#define KEY_R 0x72
|
||||||
#define KEY_S 0x73
|
#define KEY_S 0x73
|
||||||
#define KEY_T 0x74
|
#define KEY_T 0x74
|
||||||
#define KEY_U 0x55
|
#define KEY_U 0x55
|
||||||
|
|
@ -111,12 +113,12 @@
|
||||||
#define KEY_Y 0x79
|
#define KEY_Y 0x79
|
||||||
#define KEY_Z 0x7a
|
#define KEY_Z 0x7a
|
||||||
|
|
||||||
enum line_mode_t
|
typedef enum
|
||||||
{
|
{
|
||||||
LINE_OFF,
|
LINE_OFF,
|
||||||
LINE_TOGGLE,
|
LINE_TOGGLE,
|
||||||
LINE_PULSE
|
LINE_PULSE
|
||||||
};
|
} tty_line_mode_t;
|
||||||
|
|
||||||
const char random_array[] =
|
const char random_array[] =
|
||||||
{
|
{
|
||||||
|
|
@ -424,19 +426,43 @@ 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;
|
int state;
|
||||||
|
|
||||||
if (line_mode == LINE_TOGGLE)
|
|
||||||
{
|
|
||||||
// Toggle line
|
|
||||||
if (ioctl(fd, TIOCMGET, &state) < 0)
|
if (ioctl(fd, TIOCMGET, &state) < 0)
|
||||||
{
|
{
|
||||||
tio_warning_printf("Could not get line state (%s)", strerror(errno));
|
tio_warning_printf("Could not get line state (%s)", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
state &= ~mask;
|
||||||
|
tio_printf("Setting %s to HIGH", name);
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (state & mask)
|
||||||
{
|
{
|
||||||
state &= ~mask;
|
state &= ~mask;
|
||||||
|
|
@ -447,44 +473,40 @@ static void toggle_line(const char *line_name, int mask, enum line_mode_t line_m
|
||||||
state |= mask;
|
state |= mask;
|
||||||
tio_printf("Setting %s to LOW", line_name);
|
tio_printf("Setting %s to LOW", line_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(fd, TIOCMSET, &state) < 0)
|
if (ioctl(fd, TIOCMSET, &state) < 0)
|
||||||
|
{
|
||||||
tio_warning_printf("Could not set line state (%s)", strerror(errno));
|
tio_warning_printf("Could not set line state (%s)", strerror(errno));
|
||||||
}
|
}
|
||||||
} else if (line_mode == LINE_PULSE)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
if (duration > 0)
|
||||||
{
|
{
|
||||||
tio_printf("Waiting %d ms", duration);
|
tio_printf("Waiting %d ms", duration);
|
||||||
delay(duration);
|
delay(duration);
|
||||||
}
|
}
|
||||||
toggle_line(line_name, mask, LINE_TOGGLE);
|
|
||||||
|
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;
|
char unused_char;
|
||||||
bool unused_bool;
|
bool unused_bool;
|
||||||
int state;
|
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;
|
static char previous_char = 0;
|
||||||
|
|
||||||
/* Ignore unused arguments */
|
/* Ignore unused arguments */
|
||||||
|
|
@ -534,29 +556,29 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
forward = &unused_bool;
|
forward = &unused_bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle tty line toggle and pulse action
|
||||||
if (line_mode)
|
if (line_mode)
|
||||||
{
|
{
|
||||||
// Handle line toggle number action
|
|
||||||
*forward = false;
|
*forward = false;
|
||||||
switch (input_char)
|
switch (input_char)
|
||||||
{
|
{
|
||||||
case KEY_0:
|
case KEY_0:
|
||||||
toggle_line("DTR", TIOCM_DTR, line_mode);
|
tty_line_poke(fd, "DTR", TIOCM_DTR, line_mode, option.dtr_pulse_duration);
|
||||||
break;
|
break;
|
||||||
case KEY_1:
|
case KEY_1:
|
||||||
toggle_line("RTS", TIOCM_RTS, line_mode);
|
tty_line_poke(fd, "RTS", TIOCM_RTS, line_mode, option.rts_pulse_duration);
|
||||||
break;
|
break;
|
||||||
case KEY_2:
|
case KEY_2:
|
||||||
toggle_line("CTS", TIOCM_CTS, line_mode);
|
tty_line_poke(fd, "CTS", TIOCM_CTS, line_mode, option.cts_pulse_duration);
|
||||||
break;
|
break;
|
||||||
case KEY_3:
|
case KEY_3:
|
||||||
toggle_line("DSR", TIOCM_DSR, line_mode);
|
tty_line_poke(fd, "DSR", TIOCM_DSR, line_mode, option.dsr_pulse_duration);
|
||||||
break;
|
break;
|
||||||
case KEY_4:
|
case KEY_4:
|
||||||
toggle_line("DCD", TIOCM_CD, line_mode);
|
tty_line_poke(fd, "DCD", TIOCM_CD, line_mode, option.dcd_pulse_duration);
|
||||||
break;
|
break;
|
||||||
case KEY_5:
|
case KEY_5:
|
||||||
toggle_line("RI", TIOCM_RI, line_mode);
|
tty_line_poke(fd, "RI", TIOCM_RI, line_mode, option.ri_pulse_duration);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tio_warning_printf("Invalid line number");
|
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 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 p Pulse serial port line", option.prefix_key);
|
||||||
tio_printf(" ctrl-%c q Quit", 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 s Show statistics", option.prefix_key);
|
||||||
tio_printf(" ctrl-%c t Toggle line timestamp mode", 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);
|
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 upon ctrl-t q sequence */
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
case KEY_R:
|
||||||
|
/* Run script */
|
||||||
|
script_run(fd);
|
||||||
|
break;
|
||||||
|
|
||||||
case KEY_S:
|
case KEY_S:
|
||||||
/* Show tx/rx statistics upon ctrl-t s sequence */
|
/* Show tx/rx statistics upon ctrl-t s sequence */
|
||||||
tio_printf("Statistics:");
|
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 */
|
/* Input loop */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define LINE_HIGH true
|
||||||
|
#define LINE_LOW false
|
||||||
|
|
||||||
extern bool interactive_mode;
|
extern bool interactive_mode;
|
||||||
extern bool map_i_nl_cr;
|
extern bool map_i_nl_cr;
|
||||||
extern bool map_i_cr_nl;
|
extern bool map_i_cr_nl;
|
||||||
|
|
@ -36,3 +39,5 @@ void tty_wait_for_device(void);
|
||||||
void list_serial_devices(void);
|
void list_serial_devices(void);
|
||||||
void tty_input_thread_create(void);
|
void tty_input_thread_create(void);
|
||||||
void tty_input_thread_wait_ready(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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue