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()
This commit is contained in:
Martin Lund 2024-04-06 09:17:01 +02:00
parent 4369d5b66f
commit 3e50191107
4 changed files with 167 additions and 77 deletions

View file

@ -383,6 +383,14 @@ Toggle the tty line.
Sleep for seconds. Sleep for seconds.
.IP "\fBmsleep(ms)" .IP "\fBmsleep(ms)"
Sleep for miliseconds. 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 .TP 0n
Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI

View file

@ -27,6 +27,7 @@
#include <lauxlib.h> #include <lauxlib.h>
#include <lualib.h> #include <lualib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "misc.h"
#include "print.h" #include "print.h"
#include "options.h" #include "options.h"
#include "tty.h" #include "tty.h"
@ -67,60 +68,6 @@ static int msleep(lua_State *L)
return 0; 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) // lua: high(line)
static int high(lua_State *L) static int high(lua_State *L)
{ {
@ -131,7 +78,7 @@ static int high(lua_State *L)
return 0; return 0;
} }
script_line_set(line, LINE_HIGH); tty_line_set(serial_fd, line, LINE_HIGH);
return 0; return 0;
} }
@ -146,7 +93,7 @@ static int low(lua_State *L)
return 0; return 0;
} }
script_line_set(line, LINE_LOW); tty_line_set(serial_fd, line, LINE_LOW);
return 0; return 0;
} }
@ -161,7 +108,47 @@ static int toggle(lua_State *L)
return 0; 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; return 0;
} }
@ -186,6 +173,9 @@ static const struct luaL_Reg tio_lib[] =
{ "high", high}, { "high", high},
{ "low", low}, { "low", low},
{ "toggle", toggle}, { "toggle", toggle},
{ "config_high", config_high},
{ "config_low", config_low},
{ "config_apply", config_apply},
{NULL, NULL} {NULL, NULL}
}; };

126
src/tty.c
View file

@ -120,6 +120,13 @@ typedef enum
LINE_PULSE LINE_PULSE
} tty_line_mode_t; } tty_line_mode_t;
typedef struct
{
int mask;
bool value;
bool reserved;
} tty_line_config_t;
const char random_array[] = const char random_array[] =
{ {
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x20, 0x28, 0x0A, 0x20, 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 int pipefd[2];
static pthread_mutex_t mutex_input_ready = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t mutex_input_ready = PTHREAD_MUTEX_INITIALIZER;
static char line[LINE_SIZE_MAX]; static char line[LINE_SIZE_MAX];
static tty_line_config_t line_config[6] = { };
static void optional_local_echo(char c) 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; int state;
@ -440,12 +530,12 @@ void tty_line_set(int fd, const char *name, int mask, bool value)
if (value) if (value)
{ {
state &= ~mask; state &= ~mask;
tio_printf("Setting %s to HIGH", name); tio_printf("Setting %s to HIGH", tty_line_name(mask));
} }
else else
{ {
state |= mask; 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) 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; int state;
@ -467,12 +557,12 @@ void tty_line_toggle(int fd, const char *line_name, int mask)
if (state & mask) if (state & mask)
{ {
state &= ~mask; state &= ~mask;
tio_printf("Setting %s to HIGH", line_name); tio_printf("Setting %s to HIGH", tty_line_name(mask));
} }
else else
{ {
state |= mask; 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) 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) if (duration > 0)
{ {
@ -491,19 +581,19 @@ static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int
delay(duration); 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) switch (mode)
{ {
case LINE_TOGGLE: case LINE_TOGGLE:
tty_line_toggle(fd, name, mask); tty_line_toggle(fd, mask);
break; break;
case LINE_PULSE: case LINE_PULSE:
tty_line_pulse(fd, name, mask, duration); tty_line_pulse(fd, mask, duration);
break; break;
case LINE_OFF: case LINE_OFF:
@ -564,22 +654,22 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
switch (input_char) switch (input_char)
{ {
case KEY_0: 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; break;
case KEY_1: 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; break;
case KEY_2: 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; break;
case KEY_3: 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; break;
case KEY_4: 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; break;
case KEY_5: 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; break;
default: default:
tio_warning_printf("Invalid line number"); tio_warning_printf("Invalid line number");

View file

@ -39,5 +39,7 @@ 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_set(int fd, int mask, bool value);
void tty_line_toggle(int fd, const char *name, int mask); void tty_line_toggle(int fd, int mask);
void tty_line_config(int mask, bool value);
void tty_line_config_apply(void);