/*
- * tio - a simple TTY terminal I/O application
+ * tio - a simple TTY terminal I/O tool
*
- * Copyright (c) 2014-2017 Martin Lund
+ * Copyright (c) 2014-2022 Martin Lund
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/file.h>
+#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <stdbool.h>
#include <errno.h>
#include <time.h>
+#include <dirent.h>
#include "config.h"
-#include "tio/tty.h"
-#include "tio/print.h"
-#include "tio/options.h"
-#include "tio/time.h"
-#include "tio/log.h"
-#include "tio/error.h"
+#include "tty.h"
+#include "print.h"
+#include "options.h"
+#include "misc.h"
+#include "log.h"
+#include "error.h"
#ifdef HAVE_TERMIOS2
extern int setspeed2(int fd, int baudrate);
#endif
+#ifdef __APPLE__
+#define PATH_SERIAL_DEVICES "/dev/"
+#else
+#define PATH_SERIAL_DEVICES "/dev/serial/by-id/"
+#endif
+
static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old;
static unsigned long rx_total = 0, tx_total = 0;
static bool connected = false;
-static bool tainted = false;
static bool print_mode = NORMAL;
static bool standard_baudrate = true;
static void (*print)(char c);
static bool map_o_nl_crnl = false;
static bool map_o_del_bs = false;
-#define tio_printf(format, args...) \
-{ \
- if (tainted) putchar('\n'); \
- color_printf("[tio %s] " format, current_time(), ## args); \
- tainted = false; \
-}
-static void print_hex(char c)
+static void toggle_line(const char *line_name, int mask)
{
- printf("%02x ", (unsigned char) c);
-}
+ int state;
-static void print_normal(char c)
-{
- putchar(c);
+ if (ioctl(fd, TIOCMGET, &state) < 0)
+ {
+ error_printf("Could not get line state: %s", strerror(errno));
+ }
+ else
+ {
+ if (state & mask)
+ {
+ state &= ~mask;
+ tio_printf("set %s to LOW", line_name);
+ }
+ else
+ {
+ state |= mask;
+ tio_printf("set %s to HIGH", line_name);
+ }
+ if (ioctl(fd, TIOCMSET, &state) < 0)
+ error_printf("Could not set line state: %s", strerror(errno));
+ }
}
void handle_command_sequence(char input_char, char previous_char, char *output_char, bool *forward)
{
char unused_char;
bool unused_bool;
+ int state;
/* Ignore unused arguments */
if (output_char == NULL)
tio_printf(" ctrl-t ? List available key commands");
tio_printf(" ctrl-t b Send break");
tio_printf(" ctrl-t c Show configuration");
+ tio_printf(" ctrl-t d Toggle DTR line");
tio_printf(" ctrl-t e Toggle local echo mode");
tio_printf(" ctrl-t h Toggle hexadecimal mode");
tio_printf(" ctrl-t l Clear screen");
+ tio_printf(" ctrl-t L Show lines state");
tio_printf(" ctrl-t q Quit");
+ tio_printf(" ctrl-t r Toggle RTS line");
tio_printf(" ctrl-t s Show statistics");
tio_printf(" ctrl-t t Send ctrl-t key code");
- tio_printf(" ctrl-t T Toggle timestamps");
+ tio_printf(" ctrl-t T Toggle line timestamp mode");
+ tio_printf(" ctrl-t v Show version");
+ break;
+
+ case KEY_SHIFT_L:
+ if (ioctl(fd, TIOCMGET, &state) < 0)
+ {
+ error_printf("Could not get line state: %s", strerror(errno));
+ break;
+ }
+ tio_printf("Lines state:");
+ tio_printf(" DTR: %s", (state & TIOCM_DTR) ? "HIGH" : "LOW");
+ tio_printf(" RTS: %s", (state & TIOCM_RTS) ? "HIGH" : "LOW");
+ tio_printf(" CTS: %s", (state & TIOCM_CTS) ? "HIGH" : "LOW");
+ tio_printf(" DSR: %s", (state & TIOCM_DSR) ? "HIGH" : "LOW");
+ tio_printf(" DCD: %s", (state & TIOCM_CD) ? "HIGH" : "LOW");
+ tio_printf(" RI : %s", (state & TIOCM_RI) ? "HIGH" : "LOW");
+ break;
+ case KEY_D:
+ toggle_line("DTR", TIOCM_DTR);
+ break;
+
+ case KEY_R:
+ toggle_line("RTS", TIOCM_RTS);
break;
case KEY_B:
tio_printf(" Flow: %s", option.flow);
tio_printf(" Stopbits: %d", option.stopbits);
tio_printf(" Parity: %s", option.parity);
- tio_printf(" Local Echo: %s", option.local_echo ? "yes":"no");
- tio_printf(" Timestamps: %s", option.timestamp ? "yes" : "no");
+ tio_printf(" Local echo: %s", option.local_echo ? "enabled" : "disabled");
+ tio_printf(" Timestamps: %s", option.timestamp ? "enabled" : "disabled");
tio_printf(" Output delay: %d", option.output_delay);
+ tio_printf(" Auto connect: %s", option.no_autoconnect ? "disabled" : "enabled");
if (option.map[0] != 0)
tio_printf(" Map flags: %s", option.map);
if (option.log)
case KEY_S:
/* Show tx/rx statistics upon ctrl-t s sequence */
tio_printf("Statistics:");
- tio_printf(" Sent %lu bytes, received %lu bytes", tx_total, rx_total);
+ tio_printf(" Sent %lu bytes", tx_total);
+ tio_printf(" Received %lu bytes", rx_total);
break;
case KEY_T:
option.timestamp = !option.timestamp;
break;
+ case KEY_V:
+ tio_printf("tio v%s", VERSION);
+ break;
+
default:
/* Ignore unknown ctrl-t escaped keys */
break;
{
int status;
+ /* Disable line buffering in stdout. This is necessary if we
+ * want things like local echo to work correctly. */
+ setbuf(stdout, NULL);
+
/* Save current stdout settings */
if (tcgetattr(STDOUT_FILENO, &stdout_old) < 0)
{
exit(EXIT_FAILURE);
}
- /* Print launch hints */
- tio_printf("tio v%s", VERSION);
- tio_printf("Press ctrl-t q to quit");
-
/* At start use normal print function */
print = print_normal;
* Only switch cases for baud rates detected supported by the host
* system are inserted.
*
- * To see which baud rates are being probed see configure.ac
+ * To see which baud rates are being probed see meson.build
*/
- AUTOCONF_BAUDRATE_CASES
+ BAUDRATE_CASES
default:
#ifdef HAVE_TERMIOS2
}
// Set output speed
- cfsetospeed(&tio, baudrate);
+ status = cfsetospeed(&tio, baudrate);
if (status == -1)
{
error_printf("Could not configure output speed (%s)", strerror(errno));
struct timeval tv;
static char input_char, previous_char = 0;
static bool first = true;
+ static int last_errno = 0;
/* Loop until device pops up */
while (true)
}
/* Test for accessible device file */
- if (access(option.tty_device, R_OK) == 0)
+ status = access(option.tty_device, R_OK);
+ if (status == 0) {
+ last_errno = 0;
return;
+ }
+ else if (last_errno != errno)
+ {
+ warning_printf("Could not open tty device (%s)", strerror(errno));
+ tio_printf("Waiting for tty device..");
+ last_errno = errno;
+ }
}
}
if (!option.local_echo)
return;
print(c);
+ fflush(stdout);
if (option.log)
log_write(c);
}
static char previous_char = 0;
static bool first = true;
int status;
- time_t next_timestamp = 0;
+ bool next_timestamp = false;
+ char* now = NULL;
/* Open tty device */
#ifdef __APPLE__
/* Print connect status */
tio_printf("Connected");
connected = true;
- tainted = false;
+ print_tainted = false;
if (option.timestamp)
- next_timestamp = time(NULL);
+ next_timestamp = true;
/* Save current port settings */
if (tcgetattr(fd, &tio_old) < 0)
/* Print timestamp on new line, if desired. */
if (next_timestamp && input_char != '\n' && input_char != '\r')
{
- fprintf(stdout, ANSI_COLOR_GRAY "[%s] " ANSI_COLOR_RESET, current_time());
- next_timestamp = 0;
+ now = current_time();
+ if (now)
+ {
+ ansi_printf_raw("[%s] ", now);
+ if (option.log)
+ {
+ log_write('[');
+ while (*now != '\0')
+ {
+ log_write(*now);
+ ++now;
+ }
+ log_write(']');
+ log_write(' ');
+ }
+ next_timestamp = false;
+ }
}
/* Map input character */
print('\r');
print('\n');
if (option.timestamp)
- next_timestamp = time(NULL);
+ next_timestamp = true;
} else
{
/* Print received tty character to stdout */
if (option.log)
log_write(input_char);
- tainted = true;
+ print_tainted = true;
if (input_char == '\n' && option.timestamp)
- next_timestamp = time(NULL);
+ next_timestamp = true;
} else
{
/* Error reading - device is likely unplugged */
output_char = '\n';
/* Map newline character */
- if ((output_char == '\n') && (map_o_nl_crnl)) {
- char r = '\r';
+ if ((output_char == '\n' || output_char == '\r') && (map_o_nl_crnl)) {
+ const char *crlf = "\r\n";
- optional_local_echo(r);
- status = write(fd, &r, 1);
+ optional_local_echo(crlf[0]);
+ optional_local_echo(crlf[1]);
+ status = write(fd, crlf, 2);
if (status < 0)
warning_printf("Could not write to tty device");
- tx_total++;
+ tx_total += 2;
delay(option.output_delay);
- }
-
- /* Send output to tty device */
- optional_local_echo(output_char);
- status = write(fd, &output_char, 1);
- if (status < 0)
- warning_printf("Could not write to tty device");
- fsync(fd);
+ } else
+ {
+ /* Send output to tty device */
+ optional_local_echo(output_char);
+ status = write(fd, &output_char, 1);
+ if (status < 0)
+ warning_printf("Could not write to tty device");
+ fsync(fd);
- /* Update transmit statistics */
- tx_total++;
+ /* Update transmit statistics */
+ tx_total++;
- /* Insert output delay */
- delay(option.output_delay);
+ /* Insert output delay */
+ delay(option.output_delay);
+ }
}
/* Save previous key */
error_open:
return TIO_ERROR;
}
+
+void list_serial_devices(void)
+{
+ DIR *d = opendir(PATH_SERIAL_DEVICES);
+ if (d)
+ {
+ struct dirent *dir;
+ while ((dir = readdir(d)) != NULL)
+ {
+ if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, "..")))
+ {
+#ifdef __APPLE__
+#define TTY_DEVICES_PREFIX "tty."
+ if (!strncmp(dir->d_name, TTY_DEVICES_PREFIX, sizeof(TTY_DEVICES_PREFIX) - 1))
+#endif
+ printf("%s%s\n", PATH_SERIAL_DEVICES, dir->d_name);
+ }
+ }
+ closedir(d);
+ }
+}