2 * tio - a simple TTY terminal I/O application
4 * Copyright (c) 2014-2016 Martin Lund
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 #include <sys/types.h>
30 #include <sys/param.h>
38 #include "tio/print.h"
39 #include "tio/options.h"
42 #include "tio/error.h"
44 static struct termios tio, new_stdout, old_stdout, old_tio;
45 static unsigned long rx_total = 0, tx_total = 0;
46 static bool connected = false;
47 static bool tainted = false;
48 static bool standard_baudrate = true;
52 #define BOTHER 0010000
55 #define tio_printf(format, args...) \
57 if (tainted) putchar('\n'); \
58 color_printf("[tio %s] " format, current_time(), ## args); \
62 void handle_command_sequence(char input_char, char previous_char, char *output_char, bool *forward)
67 /* Ignore unused arguments */
68 if (output_char == NULL)
69 output_char = &unused_char;
72 forward = &unused_bool;
74 /* Handle escape key commands */
75 if (previous_char == KEY_CTRL_T)
80 tio_printf("Key commands:");
81 tio_printf(" ctrl-t ? List available key commands");
82 tio_printf(" ctrl-t i Show settings information");
83 tio_printf(" ctrl-t q Quit");
84 tio_printf(" ctrl-t s Show statistics");
85 tio_printf(" ctrl-t t Send ctrl-t key code");
89 tio_printf("Settings information:");
90 tio_printf(" TTY device: %s", option.tty_device);
91 tio_printf(" Baudrate: %u", option.baudrate);
92 tio_printf(" Databits: %d", option.databits);
93 tio_printf(" Flow: %s", option.flow);
94 tio_printf(" Stopbits: %d", option.stopbits);
95 tio_printf(" Parity: %s", option.parity);
96 tio_printf(" Output delay: %d", option.output_delay);
98 tio_printf(" Log file: %s", option.log_filename);
102 /* Exit upon ctrl-t q sequence */
105 /* Send ctrl-t key code upon ctrl-t t sequence */
106 *output_char = KEY_CTRL_T;
109 /* Show tx/rx statistics upon ctrl-t s sequence */
110 tio_printf("Statistics:");
111 tio_printf(" Sent %lu bytes, received %lu bytes", tx_total, rx_total);
115 /* Ignore unknown ctrl-t escaped keys */
122 void stdout_configure(void)
124 /* Save current stdout settings */
125 if (tcgetattr(STDOUT_FILENO, &old_stdout) < 0)
127 error_printf("Saving current stdio settings failed");
131 /* Prepare new stdout settings */
132 memset(&new_stdout, 0, sizeof(new_stdout));
134 /* Control, input, output, local modes for stdout */
135 new_stdout.c_cflag = 0;
136 new_stdout.c_iflag = 0;
137 new_stdout.c_oflag = 0;
138 new_stdout.c_lflag = 0;
140 /* Control characters */
141 new_stdout.c_cc[VTIME] = 0; /* Inter-character timer unused */
142 new_stdout.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
144 /* Activate new stdout settings */
145 tcsetattr(STDOUT_FILENO, TCSANOW, &new_stdout);
146 tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new_stdout);
148 /* Print launch hints */
149 tio_printf("tio v%s", VERSION);
150 tio_printf("Press ctrl-t q to quit");
152 /* Make sure we restore old stdout settings on exit */
153 atexit(&stdout_restore);
156 void stdout_restore(void)
158 tcsetattr(STDOUT_FILENO, TCSANOW, &old_stdout);
159 tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old_stdout);
162 void tty_configure(void)
164 speed_t baudrate = 0;
166 memset(&tio, 0, sizeof(tio));
169 switch (option.baudrate)
171 /* The macro below expands into switch cases autogenerated by the
172 * configure script. Each switch case verifies and configures the baud
173 * rate and is of the form:
175 * case $baudrate: baudrate = B$baudrate; break;
177 * Only switch cases for baud rates detected supported by the host
178 * system are inserted.
180 * To see which baud rates are being probed see configure.ac
182 AUTOCONF_BAUDRATE_CASES
185 #if !HAVE_DECL_BOTHER
186 error_printf("Invalid baud rate");
189 standard_baudrate = false;
194 if (standard_baudrate)
196 cfsetispeed(&tio, baudrate);
197 cfsetospeed(&tio, baudrate);
200 tio.c_ispeed = tio.c_ospeed = baudrate;
201 tio.c_cflag &= ~CBAUD;
202 tio.c_cflag |= BOTHER;
206 tio.c_cflag &= ~CSIZE;
207 switch (option.databits)
222 error_printf("Invalid data bits");
226 /* Set flow control */
227 if (strcmp("hard", option.flow) == 0)
229 tio.c_cflag |= CRTSCTS;
230 tio.c_iflag &= ~(IXON | IXOFF | IXANY);
232 else if (strcmp("soft", option.flow) == 0)
234 tio.c_cflag &= ~CRTSCTS;
235 tio.c_iflag |= IXON | IXOFF;
237 else if (strcmp("none", option.flow) == 0)
239 tio.c_cflag &= ~CRTSCTS;
240 tio.c_iflag &= ~(IXON | IXOFF | IXANY);
244 error_printf("Invalid flow control");
249 switch (option.stopbits)
252 tio.c_cflag &= ~CSTOPB;
255 tio.c_cflag |= CSTOPB;
258 error_printf("Invalid stop bits");
263 if (strcmp("odd", option.parity) == 0)
265 tio.c_cflag |= PARENB;
266 tio.c_cflag |= PARODD;
268 else if (strcmp("even", option.parity) == 0)
270 tio.c_cflag |= PARENB;
271 tio.c_cflag &= ~PARODD;
273 else if (strcmp("none", option.parity) == 0)
274 tio.c_cflag &= ~PARENB;
277 error_printf("Invalid parity");
282 void tty_wait_for_device(void)
287 static char input_char, previous_char = 0;
288 static bool first = true;
290 /* Loop until device pops up */
295 /* Don't wait first time */
301 /* Wait up to 1 second */
307 FD_SET(STDIN_FILENO, &rdfs);
309 /* Block until input becomes available or timeout */
310 status = select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
313 /* Input from stdin ready */
315 /* Read one character */
316 status = read(STDIN_FILENO, &input_char, 1);
319 error_printf("Could not read from stdin");
323 /* Handle commands */
324 handle_command_sequence(input_char, previous_char, NULL, NULL);
326 previous_char = input_char;
328 } else if (status == -1)
330 error_printf("select() failed (%s)", strerror(errno));
334 /* Test for accessible device file */
335 if (access(option.tty_device, R_OK) == 0)
340 void tty_disconnect(void)
344 tio_printf("Disconnected");
351 void tty_restore(void)
353 tcsetattr(fd, TCSANOW, &old_tio);
354 tcsetattr(fd, TCSAFLUSH, &old_tio);
360 int tty_connect(void)
362 fd_set rdfs; /* Read file descriptor set */
363 int maxfd; /* Maximum file descriptor used */
364 char input_char, output_char;
365 static char previous_char = 0;
366 static bool first = true;
369 /* Open tty device */
370 fd = open(option.tty_device, O_RDWR | O_NOCTTY );
373 error_printf_silent("Could not open tty device (%s)", strerror(errno));
377 /* Make sure device is of tty type */
380 error_printf("Not a tty device");
384 /* Lock device file */
385 status = flock(fd, LOCK_EX | LOCK_NB);
386 if ((status == -1) && (errno == EWOULDBLOCK))
388 error_printf("Device file is locked by another process");
392 /* Flush stale I/O data (if any) */
393 tcflush(fd, TCIOFLUSH);
395 /* Warn if non standard baud rate is used */
396 if (!standard_baudrate)
397 tio_printf("Warning: Using a non standard baud rate");
399 /* Print connect status */
400 tio_printf("Connected");
404 /* Save current port settings */
405 if (tcgetattr(fd, &old_tio) < 0)
406 goto error_tcgetattr;
408 /* Make sure we restore tty settings on exit */
411 atexit(&tty_restore);
415 /* Control, input, output, local modes for tty device */
416 tio.c_cflag |= CLOCAL | CREAD;
420 /* Control characters */
421 tio.c_cc[VTIME] = 0; /* Inter-character timer unused */
422 tio.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
424 /* Activate new port settings */
425 tcsetattr(fd, TCSANOW, &tio);
426 tcsetattr(fd, TCSAFLUSH, &tio);
428 maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */
435 FD_SET(STDIN_FILENO, &rdfs);
437 /* Block until input becomes available */
438 status = select(maxfd, &rdfs, NULL, NULL, NULL);
441 if (FD_ISSET(fd, &rdfs))
443 /* Input from tty device ready */
444 if (read(fd, &input_char, 1) > 0)
446 /* Update receive statistics */
449 /* Print received tty character to stdout */
455 log_write(input_char);
461 /* Error reading - device is likely unplugged */
462 error_printf_silent("Could not read from tty device");
466 if (FD_ISSET(STDIN_FILENO, &rdfs))
470 /* Input from stdin ready */
471 status = read(STDIN_FILENO, &input_char, 1);
474 error_printf_silent("Could not read from stdin");
478 /* Forward input to output except ctrl-t key */
479 output_char = input_char;
480 if (input_char == KEY_CTRL_T)
483 /* Handle commands */
484 handle_command_sequence(input_char, previous_char, &output_char, &forward);
488 /* Send output to tty device */
489 status = write(fd, &output_char, 1);
491 warning_printf("Could not write to tty device");
493 /* Update transmit statistics */
496 /* Insert output delay */
497 delay(option.output_delay);
500 /* Save previous key */
501 previous_char = input_char;
504 } else if (status == -1)
506 error_printf("Error: select() failed (%s)", strerror(errno));