diff --git a/README.md b/README.md index dfadf95..fd43265 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ when used in combination with [tmux](https://tmux.github.io). * List available serial devices by ID * Show RX/TX statistics * Toggle serial lines - * Pulse the DTR line + * Pulse serial lines with configurable duration * Local echo support * Map characters (nl, cr-nl, bs, lowercase to uppercase, etc.) * Line timestamps @@ -72,7 +72,7 @@ The command-line interface is straightforward as reflected in the output from -p, --parity odd|even|none|mark|space Parity (default: none) -o, --output-delay Output character delay (default: 0) -O, --output-line-delay Output line delay (default: 0) - --dtr-pulse-duration DTR pulse duration (default: 100) + --line-pulse-duration Set line pulse duration -n, --no-autoconnect Disable automatic connect -e, --local-echo Enable local echo -t, --timestamp Enable line timestamp @@ -131,16 +131,14 @@ ctrl-t ? to list the available key commands. [20:19:12.040] ctrl-t ? List available key commands [20:19:12.040] ctrl-t b Send break [20:19:12.040] ctrl-t c Show configuration -[20:19:12.040] ctrl-t d Toggle DTR line -[20:19:12.040] ctrl-t D Pulse DTR line [20:19:12.040] ctrl-t e Toggle local echo mode +[20:19:12.040] ctrl-t g Toggle serial port line [20:19:12.040] ctrl-t h Toggle hexadecimal mode [20:19:12.040] ctrl-t l Clear screen [20:19:12.040] ctrl-t L Show line states +[20:19:12.040] ctrl-t p Pulse serial port line [20:19:12.040] ctrl-t q Quit -[20:19:12.040] ctrl-t r Toggle RTS line [20:19:12.041] ctrl-t s Show statistics -[20:19:12.041] ctrl-t t Send ctrl-t key code [20:19:12.041] ctrl-t T Toggle line timestamp mode [20:19:12.041] ctrl-t U Toggle conversion to uppercase [20:19:12.041] ctrl-t v Show version @@ -169,7 +167,6 @@ databits = 8 parity = none stopbits = 1 color = 10 -dtr-pulse-duration = 50 [rpi3] tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 @@ -177,6 +174,7 @@ baudrate = 115200 no-autoconnect = enable log = enable log-file = rpi3.log +line-pulse-duration = DTR=200,RTS=150 color = 12 [usb devices] diff --git a/example/tiorc b/example/tiorc index 767b4f8..adbc4e1 100644 --- a/example/tiorc +++ b/example/tiorc @@ -30,6 +30,7 @@ color = 9 [am64-evm] baudrate = 115200 tty = /dev/serial/by-id/usb-Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_01093176-if01-port0 +line-pulse-duration = DTR=200,RTS=300,RI=50 color = 10 [tincan] diff --git a/man/tio.1.in b/man/tio.1.in index 680f27c..48623b2 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -52,9 +52,26 @@ Set output delay [ms] inserted between each sent character (default: 0). Set output delay [ms] inserted between each sent line (default: 0). .TP -.BR " \-\-dtr\-pulse\-duration " \fI +.BR " \-\-line\-pulse\-duration " \fI -Set the duration [ms] of the DTR pulse (default: 100). +Set the pulse duration [ms] of each serial port line using the following key +valur pair format in the duration field: = + +Each key represents a serial line. The following keys are available: + +.RS +.TP 12n +.IP "\fBDTR - Data Terminal Ready" +.IP "\fBRTS - Request To Send" +.IP "\fBCTS - Clear To Send" +.IP "\fBDSR - Data Set Ready" +.IP "\fBDCD - Data Carrier Detect" +.IP "\fBRI - Ring Indicator" +.P +If defining more than one key value pair, the pairs must be comma separated. + +The default pulse duration for each line is 100 ms. +.RE .TP .BR \-n ", " \-\-no\-autoconnect @@ -211,22 +228,20 @@ Send serial break (triggers SysRq on Linux, etc.) Show configuration (baudrate, databits, etc.) .IP "\fBctrl-t e" Toggle local echo mode +.IP "\fBctrl-t g" +Toggle serial port line .IP "\fBctrl-t h" Toggle hexadecimal mode .IP "\fBctrl-t l" Clear screen +.IP "\fBctrl-t L" +Show line states (DTR, RTS, CTS, DSR, DCD, RI) +.IP "\fBctrl-t p" +Pulse serial port line .IP "\fBctrl-t q" Quit .IP "\fBctrl-t s" Show TX/RX statistics -.IP "\fBctrl-t L" -Show line states (DTR, RTS, CTS, DSR, DCD, RI) -.IP "\fBctrl-t d" -Toggle DTR -.IP "\fBctrl-t D" -Pulse DTR -.IP "\fBctrl-t r" -Toggle RTS .IP "\fBctrl-t t" Toggle line timestamp mode .IP "\fBctrl-t U" @@ -273,7 +288,7 @@ Any options set via command-line will override options set in the configuration .PP The following configuration file options are available: -.TP 20n +.TP 21n .IP "\fBpattern" Pattern matching user input. This pattern can be an extended regular expression with a single group. .IP "\fBtty" @@ -292,8 +307,8 @@ Set parity Set output character delay .IP "\fBoutput-line-delay" Set output line delay -.IP "\fBdtr-pulse-duration" -Set DTR pulse duration +.IP "\fBline-pulse-duration" +Set line pulse duration .IP "\fBno-autoconnect" Disable automatic connect .IP "\fBlog" @@ -333,7 +348,7 @@ databits = 8 parity = none stopbits = 1 color = 10 -dtr-pulse-duration = 50 +line-pulse-duration = DTR=200,RTS=400 .ec .fi .RE diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 6ed5520..3d87cb6 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -17,7 +17,7 @@ _tio() -p --parity \ -o --output-delay \ -o --output-line-delay \ - --dtr-pulse-duration \ + --line-pulse-duration \ -n --no-autoconnect \ -e --local-echo \ -l --log \ @@ -64,8 +64,8 @@ _tio() COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) ) return 0 ;; - --dtr-pulse-duration) - COMPREPLY=( $(compgen -W "10 50 100 500" -- ${cur}) ) + --line-pulse-duration) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; -n | --no-autoconnect) diff --git a/src/configfile.c b/src/configfile.c index 6eaa96a..b8a1649 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -128,9 +128,9 @@ static int data_handler(void *user, const char *section, const char *name, { option.output_line_delay = atoi(value); } - else if (!strcmp(name, "dtr-pulse-duration")) + else if (!strcmp(name, "line-pulse-duration")) { - option.dtr_pulse_duration = atoi(value); + line_pulse_duration_option_parse(value); } else if (!strcmp(name, "no-autoconnect")) { diff --git a/src/misc.c b/src/misc.c index f51c69c..c519c46 100644 --- a/src/misc.c +++ b/src/misc.c @@ -97,6 +97,11 @@ void delay(long ms) { struct timespec ts; + if (ms <= 0) + { + return; + } + ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; diff --git a/src/options.c b/src/options.c index f89fcab..b63e1f1 100644 --- a/src/options.c +++ b/src/options.c @@ -42,7 +42,7 @@ enum opt_t OPT_TIMESTAMP_FORMAT, OPT_LOG_FILE, OPT_LOG_STRIP, - OPT_DTR_PULSE_DURATION, + OPT_LINE_PULSE_DURATION, }; /* Default options */ @@ -57,6 +57,11 @@ struct option_t option = .output_delay = 0, .output_line_delay = 0, .dtr_pulse_duration = 100, + .rts_pulse_duration = 100, + .cts_pulse_duration = 100, + .dsr_pulse_duration = 100, + .dcd_pulse_duration = 100, + .ri_pulse_duration = 100, .no_autoconnect = false, .log = false, .log_filename = NULL, @@ -85,7 +90,7 @@ void print_help(char *argv[]) printf(" -p, --parity odd|even|none|mark|space Parity (default: none)\n"); printf(" -o, --output-delay Output character delay (default: 0)\n"); printf(" -O, --output-line-delay Output line delay (default: 0)\n"); - printf(" --dtr-pulse-duration DTR pulse duration (default: 100)\n"); + printf(" --line-pulse-duration Line pulse duration\n"); printf(" -n, --no-autoconnect Disable automatic connect\n"); printf(" -e, --local-echo Enable local echo\n"); printf(" -t, --timestamp Enable line timestamp\n"); @@ -163,6 +168,63 @@ enum timestamp_t timestamp_option_parse(const char *arg) return timestamp; } +void line_pulse_duration_option_parse(const char *arg) +{ + bool token_found = true; + char *token = NULL; + char *buffer = strdup(arg); + + while (token_found == true) + { + if (token == NULL) + { + token = strtok(buffer,","); + } + else + { + token = strtok(NULL, ","); + } + + if (token != NULL) + { + char keyname[10]; + unsigned int value; + sscanf(token, "%10[^=]=%d", keyname, &value); + + if (!strcmp(keyname, "DTR")) + { + option.dtr_pulse_duration = value; + } + else if (!strcmp(keyname, "RTS")) + { + option.rts_pulse_duration = value; + } + else if (!strcmp(keyname, "CTS")) + { + option.cts_pulse_duration = value; + } + else if (!strcmp(keyname, "DSR")) + { + option.dsr_pulse_duration = value; + } + else if (!strcmp(keyname, "DCD")) + { + option.dcd_pulse_duration = value; + } + else if (!strcmp(keyname, "RI")) + { + option.ri_pulse_duration = value; + } + } + else + { + token_found = false; + } + } + free(buffer); + +} + void options_print() { tio_printf(" TTY device: %s", option.tty_device); @@ -177,6 +239,12 @@ void options_print() tio_printf(" Output line delay: %d", option.output_line_delay); tio_printf(" DTR pulse duration: %d", option.dtr_pulse_duration); tio_printf(" Auto connect: %s", option.no_autoconnect ? "disabled" : "enabled"); + tio_printf(" Pulse duration: DTR=%d RTS=%d CTS=%d DSR=%d DCD=%d RI=%d", option.dtr_pulse_duration, + option.rts_pulse_duration, + option.cts_pulse_duration, + option.dsr_pulse_duration, + option.dcd_pulse_duration, + option.ri_pulse_duration); if (option.map[0] != 0) tio_printf(" Map flags: %s", option.map); if (option.log) @@ -199,29 +267,29 @@ void options_parse(int argc, char *argv[]) { static struct option long_options[] = { - {"baudrate", required_argument, 0, 'b' }, - {"databits", required_argument, 0, 'd' }, - {"flow", required_argument, 0, 'f' }, - {"stopbits", required_argument, 0, 's' }, - {"parity", required_argument, 0, 'p' }, - {"output-delay", required_argument, 0, 'o' }, - {"output-line-delay", required_argument, 0, 'O' }, - {"dtr-pulse-duration", required_argument, 0, OPT_DTR_PULSE_DURATION}, - {"no-autoconnect", no_argument, 0, 'n' }, - {"local-echo", no_argument, 0, 'e' }, - {"timestamp", no_argument, 0, 't' }, - {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT }, - {"list-devices", no_argument, 0, 'L' }, - {"log", no_argument, 0, 'l' }, - {"log-file", required_argument, 0, OPT_LOG_FILE }, - {"log-strip", no_argument, 0, OPT_LOG_STRIP }, - {"socket", required_argument, 0, 'S' }, - {"map", required_argument, 0, 'm' }, - {"color", required_argument, 0, 'c' }, - {"hexadecimal", no_argument, 0, 'x' }, - {"version", no_argument, 0, 'v' }, - {"help", no_argument, 0, 'h' }, - {0, 0, 0, 0 } + {"baudrate", required_argument, 0, 'b' }, + {"databits", required_argument, 0, 'd' }, + {"flow", required_argument, 0, 'f' }, + {"stopbits", required_argument, 0, 's' }, + {"parity", required_argument, 0, 'p' }, + {"output-delay", required_argument, 0, 'o' }, + {"output-line-delay" , required_argument, 0, 'O' }, + {"line-pulse-duration", required_argument, 0, OPT_LINE_PULSE_DURATION}, + {"no-autoconnect", no_argument, 0, 'n' }, + {"local-echo", no_argument, 0, 'e' }, + {"timestamp", no_argument, 0, 't' }, + {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT }, + {"list-devices", no_argument, 0, 'L' }, + {"log", no_argument, 0, 'l' }, + {"log-file", required_argument, 0, OPT_LOG_FILE }, + {"log-strip", no_argument, 0, OPT_LOG_STRIP }, + {"socket", required_argument, 0, 'S' }, + {"map", required_argument, 0, 'm' }, + {"color", required_argument, 0, 'c' }, + {"hexadecimal", no_argument, 0, 'x' }, + {"version", no_argument, 0, 'v' }, + {"help", no_argument, 0, 'h' }, + {0, 0, 0, 0 } }; /* getopt_long stores the option index here */ @@ -274,8 +342,8 @@ void options_parse(int argc, char *argv[]) option.output_line_delay = string_to_long(optarg); break; - case OPT_DTR_PULSE_DURATION: - option.dtr_pulse_duration = string_to_long(optarg); + case OPT_LINE_PULSE_DURATION: + line_pulse_duration_option_parse(optarg); break; case 'n': diff --git a/src/options.h b/src/options.h index fadc4b9..ed8a4fa 100644 --- a/src/options.h +++ b/src/options.h @@ -49,7 +49,12 @@ struct option_t char *parity; int output_delay; int output_line_delay; - int dtr_pulse_duration; + unsigned int dtr_pulse_duration; + unsigned int rts_pulse_duration; + unsigned int cts_pulse_duration; + unsigned int dsr_pulse_duration; + unsigned int dcd_pulse_duration; + unsigned int ri_pulse_duration; bool no_autoconnect; bool log; bool log_strip; @@ -69,3 +74,5 @@ extern struct option_t option; void options_print(); void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); + +void line_pulse_duration_option_parse(const char *arg); diff --git a/src/tty.c b/src/tty.c index 4c7407b..0902a4d 100644 --- a/src/tty.c +++ b/src/tty.c @@ -61,25 +61,37 @@ #define CMSPAR 010000000000 #endif +#define KEY_0 0x30 +#define KEY_1 0x31 +#define KEY_2 0x32 +#define KEY_3 0x33 +#define KEY_4 0x34 +#define KEY_5 0x35 #define KEY_QUESTION 0x3f #define KEY_B 0x62 #define KEY_C 0x63 #define KEY_E 0x65 +#define KEY_G 0x67 #define KEY_H 0x68 #define KEY_L 0x6C +#define KEY_P 0x70 #define KEY_Q 0x71 #define KEY_S 0x73 #define KEY_T 0x74 #define KEY_U 0x55 #define KEY_V 0x76 -#define KEY_D 0x64 -#define KEY_SHIFT_D 0x44 -#define KEY_R 0x72 #define KEY_SHIFT_L 0x4C #define NORMAL 0 #define HEX 1 +enum line_mode_t +{ + LINE_OFF, + LINE_TOGGLE, + LINE_PULSE +}; + bool interactive_mode = true; static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old; @@ -242,28 +254,67 @@ static void output_hex(char c) } } -static void toggle_line(const char *line_name, int mask) +static void toggle_line(const char *line_name, int mask, enum line_mode_t line_mode) { int state; - if (ioctl(fd, TIOCMGET, &state) < 0) + if (line_mode == LINE_TOGGLE) { - tio_warning_printf("Could not get line state (%s)", strerror(errno)); - } - else - { - if (state & mask) + // Toggle line + if (ioctl(fd, TIOCMGET, &state) < 0) { - state &= ~mask; - tio_printf("set %s to LOW", line_name); + tio_warning_printf("Could not get line state (%s)", strerror(errno)); } else { - state |= mask; - tio_printf("set %s to HIGH", line_name); + if (state & mask) + { + state &= ~mask; + tio_printf("Setting %s to LOW", line_name); + } + else + { + state |= mask; + tio_printf("Setting %s to HIGH", line_name); + } + if (ioctl(fd, TIOCMSET, &state) < 0) + tio_warning_printf("Could not set line state (%s)", strerror(errno)); } - if (ioctl(fd, TIOCMSET, &state) < 0) - 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; + } + if (duration > 0) + { + tio_printf("Waiting %d ms", duration); + delay(duration); + } + toggle_line(line_name, mask, LINE_TOGGLE); } } @@ -272,13 +323,52 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c char unused_char; bool unused_bool; int state; + static enum line_mode_t line_mode = LINE_OFF; /* Ignore unused arguments */ if (output_char == NULL) + { output_char = &unused_char; + } if (forward == NULL) + { forward = &unused_bool; + } + + if (line_mode) + { + // Handle line toggle number action + *forward = false; + switch (input_char) + { + case KEY_0: + toggle_line("DTR", TIOCM_DTR, line_mode); + break; + case KEY_1: + toggle_line("RTS", TIOCM_RTS, line_mode); + break; + case KEY_2: + toggle_line("CTS", TIOCM_CTS, line_mode); + break; + case KEY_3: + toggle_line("DSR", TIOCM_DSR, line_mode); + break; + case KEY_4: + toggle_line("DCD", TIOCM_CD, line_mode); + break; + case KEY_5: + toggle_line("RI", TIOCM_RI, line_mode); + break; + default: + tio_warning_printf("Invalid line number"); + break; + } + + line_mode = LINE_OFF; + + return; + } /* Handle escape key commands */ if (previous_char == option.prefix_code) @@ -293,14 +383,13 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c tio_printf(" ctrl-%c ? List available key commands", option.prefix_key); tio_printf(" ctrl-%c b Send break", option.prefix_key); tio_printf(" ctrl-%c c Show configuration", option.prefix_key); - tio_printf(" ctrl-%c d Toggle DTR line", option.prefix_key); - tio_printf(" ctrl-%c D Pulse DTR line", option.prefix_key); tio_printf(" ctrl-%c e Toggle local echo mode", option.prefix_key); + tio_printf(" ctrl-%c g Toggle serial port line", option.prefix_key); tio_printf(" ctrl-%c h Toggle hexadecimal mode", option.prefix_key); tio_printf(" ctrl-%c l Clear screen", option.prefix_key); tio_printf(" ctrl-%c L Show line states", 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 Toggle RTS line", 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); @@ -321,18 +410,29 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c 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); + + case KEY_G: + tio_printf("Please enter which serial line number to toggle:"); + tio_printf(" DTR (0)"); + tio_printf(" RTS (1)"); + tio_printf(" CTS (2)"); + tio_printf(" DSR (3)"); + tio_printf(" DCD (4)"); + tio_printf(" RI (5)"); + // Process next input character as part of the line toggle step + line_mode = LINE_TOGGLE; break; - case KEY_SHIFT_D: - toggle_line("DTR", TIOCM_DTR); - delay(option.dtr_pulse_duration); - toggle_line("DTR", TIOCM_DTR); - break; - - case KEY_R: - toggle_line("RTS", TIOCM_RTS); + case KEY_P: + tio_printf("Please enter which serial line number to pulse:"); + tio_printf(" DTR (0)"); + tio_printf(" RTS (1)"); + tio_printf(" CTS (2)"); + tio_printf(" DSR (3)"); + tio_printf(" DCD (4)"); + tio_printf(" RI (5)"); + // Process next input character as part of the line pulse step + line_mode = LINE_PULSE; break; case KEY_B: