g0mb4 <gomba007@gmail.com>
ZeroMemoryEx on GitHub
George Joseph <g.devel@wxy78.net>
+Robert Snell <rcsnell@ncf.ca>
+Rui Chen <rui@chenrui.dev>
+Ralph Siemsen <ralphs@netwinder.org>
+Victor Oliveira <rhapsodyv@gmail.com>
Thanks to everyone who has contributed to this project.
-=== tio v1.43 ===
+=== tio v1.47 ===
+
+
+
+Changes since tio v1.46:
+
+ * Enable log feature when using --log-filename
+
+ No reason to not assume that the user wants to enable log when the
+ --log-filename is used. This way uses can skip the use of --log to
+ enable log.
+
+ * Enable line buffering of log
+
+ Replace flushing/writing of log at every log write operation with line
+ buffering, meaning log will be written line by line to make it more I/O
+ friendly but still update frequently.
+
+ * Avoid invalid hex character messages when switching hex mode
+
+ * Force flushing of log writes
+
+ * Renamed tty_flush() to tty_sync()
+
+ * Fix sync output to serial port
+
+ Using fsync() on filedescriptors for serial ports can not be relied on.
+ Add use of tcdrain() to make sure data has been written by the serial
+ port before proceeding.
+
+ This fixes a problem with tio sometimes not writing piped input data to
+ the serial port before exiting which results in the pending writes being
+ cancelled / flushed.
+
+ * Clean up tty_flush()
+
+ * Force frequent sync on tty_flush()
+
+ * Update README
+
+ * Update example tiorc
+
+ * Quit from non-interactive mode using ctrl-c
+
+ When piping to tio it will automatically enter "non-interactive" mode
+ which means it will not react to any input key sequences but simple read
+ the input stream and write it to the tty device.
+
+ This also means that ctrl-t q can not be used to quit and so tio would
+ hang forever when used in non-interactive mode.
+
+ This change allows to send the standard termination signal by pressing
+ ctrl-c on tio in non-interactive mode to make it quit.
+
+ * Make sure we flush output buffer to tty when piping to tio
+
+ * Do not return false read error when piping to tio
+
+ * Show error message when reading port settings fail
+
+Victor Oliveira
+
+ * add macports install instructions
+
+
+
+Changes since tio v1.45:
+
+ * Rework toggle and pulse feature to support all lines
+
+ Replace existing toggle and pulse key commands with the following
+ generalized key commands which allows to toggle or pulse all serial port
+ lines:
+
+ ctrl-t g Toggle serial port line
+ ctrl-t p Pulse serial port line
+
+ When used, user will be asked which serial line to toggle or pulse.
+
+ Also introduce --line-pulse-duration option for setting specific pulse
+ duration in milliseconds for each serial line using a key value pair
+ format. Each key represents a serial line. The following keys are
+ available: DTR, RTS, CTS, DSR, DCD, RI.
+
+ Example:
+
+ $ tio /dev/ttyUSB0 --line-pulse-duration DTR=200,RTS=300,RI=50
+
+ Likewise, the pulse duration can also be set via configuration file
+ using the line-pulse-duration variable:
+
+ line-pulse-duration = DTR=200,RTS=300,RI=50
+
+ * Upgrade inih wrap to r56
+
+ * Optimization
+
+ * Add example configuration file
+
+
+Ralph Siemsen:
+
+ * Fix relative timestamps
+
+ Fix the display of relative timestamps. The hack of subtracting 3600
+ only works if you happen to be in a time zone that is one hour away from
+ UTC. When subtracting two time values, the result is an absolute
+ quantity (interval). These should be displayed as-is; without local time
+ zone nor daylight saving correction. Hence gmtime() instead of
+ localtime().
+
+
+
+Changes since tio v1.44:
+
+ * Introduce bold color option
+
+ Introduce "bold" color option which only apply bold color formatting to
+ existing system color.
+
+ Also make "bold" the default color option.
+
+ Fixes all white issue with black on white tio text.
+
+ * Update README
+
+ * Change 'ctrl-t T' to 'ctrl-t t' for timestamp toggle
+
+ * Add support for remapping prefix key
+
+ Make it possible to remap the prefix key (default: ctrl-t) by setting
+ the prefix-ctrl-key variable in the configuration file.
+
+ Allowed values are in the range a..z.
+
+ Example, to set the prefix key to ctrl-a simply do:
+
+ prefix-ctrl-key = a
+
+ * Add plaintext man page
+
+Rui Chen:
+
+ * docs: add homebrew installation note
+
+ * fix macOS build
+
+ * fix compilation error
+
+
+
+Changes since tio v1.43:
+
+ * Simplify arbitrary baudrate code
+
+ * Cleanup error printing routines
+
+ Clean up so that only the following error related printing functions are
+ used: tio_error_printf(), tio_error_printf_silent(),
+ tio_warning_printf().
+
+ A session mode switch is introduced for error printing so that it will
+ print error messages with better formatting depending on in or out of
+ session.
+
+ * Update README
+
+ * Clean up man page
+
+ * Add support for space parity
+
+ * Rename EOL delay to Output line delay
+
+ * Replace -U,--upcase with mapping flag OLTU
+
+ * Simplify tty_write()
+
+Robert Snell:
+
+ * Additional commands: EOL delay, lower to upper translation, added mark parity
+
+ Added command line options:
+ -O, --eol-delay to have a separate delay for end of line
+ -U, --upper to enable translation of lower case alpha to upper case
+
+ Added ability to set mark parity.
+ Added ctrl-t U key sequence to allow enable/disable lower case alpha to
+ upper case during a session.
+ Updated Man page with command line options, ctrl-t sequences and
+ configuration file options.
+ Updated README.md, with above information.
## 2. Features
* Easily connect to serial TTY devices
- * Automatic connect
+ * Automatic connect and reconnect
* Support for arbitrary baud rates
- * List available serial devices
+ * List available serial devices by ID
* Show RX/TX statistics
* Toggle serial lines
- * Pulse the DTR line
+ * Pulse serial lines with configurable pulse duration
* Local echo support
- * Remap special characters (nl, cr-nl, bs, etc.)
+ * Map characters (nl, cr-nl, bs, lowercase to uppercase, etc.)
* Line timestamps
- * Support for delayed output
+ * Support for delayed output per character
+ * Support for delayed output per line
* Hexadecimal mode
* Log to file
* Autogeneration of log filename
* Pipe input and/or output
* Bash completion
* Color support
+ * Remapping of prefix key
* Man page documentation
## 3. Usage
+For more usage details please see the man page documentation
+[here](man/tio.1.txt).
+
### 3.1 Command-line
The command-line interface is straightforward as reflected in the output from
'tio --help':
```
- Usage: tio [<options>] <tty-device|sub-config>
-
- Connect to tty device directly or via sub-configuration.
-
- Options:
- -b, --baudrate <bps> Baud rate (default: 115200)
- -d, --databits 5|6|7|8 Data bits (default: 8)
- -f, --flow hard|soft|none Flow control (default: none)
- -s, --stopbits 1|2 Stop bits (default: 1)
- -p, --parity odd|even|none Parity (default: none)
- -o, --output-delay <ms> Character output delay (default: 0)
- --dtr-pulse-duration <ms> DTR pulse duration (default: 100)
- -n, --no-autoconnect Disable automatic connect
- -e, --local-echo Enable local echo
- -t, --timestamp Enable line timestamp
- --timestamp-format <format> Set timestamp format (default: 24hour)
- -L, --list-devices List available serial devices
- -l, --log Enable log to file
- --log-file <filename> Set log filename
- --log-strip Strip control characters and escape sequences
- -m, --map <flags> Map special characters
- -c, --color 0..255|none|list Colorize tio text (default: 15)
- -S, --socket <socket> Redirect I/O to file or network socket
- -x, --hexadecimal Enable hexadecimal mode
- -v, --version Display version
- -h, --help Display help
-
- Options and sub-configurations may be set via configuration file.
-
- In session, press ctrl-t q to quit.
-
- See the man page for more details.
+ Usage: tio [<options>] <tty-device|sub-config>
+
+ Connect to tty device directly or via sub-configuration.
+
+ Options:
+ -b, --baudrate <bps> Baud rate (default: 115200)
+ -d, --databits 5|6|7|8 Data bits (default: 8)
+ -f, --flow hard|soft|none Flow control (default: none)
+ -s, --stopbits 1|2 Stop bits (default: 1)
+ -p, --parity odd|even|none|mark|space Parity (default: none)
+ -o, --output-delay <ms> Output character delay (default: 0)
+ -O, --output-line-delay <ms> Output line delay (default: 0)
+ --line-pulse-duration <duration> Set line pulse duration
+ -n, --no-autoconnect Disable automatic connect
+ -e, --local-echo Enable local echo
+ -t, --timestamp Enable line timestamp
+ --timestamp-format <format> Set timestamp format (default: 24hour)
+ -L, --list-devices List available serial devices
+ -l, --log Enable log to file
+ --log-file <filename> Set log filename
+ --log-strip Strip control characters and escape sequences
+ -m, --map <flags> Map characters
+ -c, --color 0..255|bold|none|list Colorize tio text (default: bold)
+ -S, --socket <socket> Redirect I/O to file or network socket
+ -x, --hexadecimal Enable hexadecimal mode
+ -v, --version Display version
+ -h, --help Display help
+
+ Options and sub-configurations may be set via configuration file.
+
+ See the man page for more details.
+
```
By default tio automatically connects to the provided TTY device if present.
[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
```
+If needed, the prefix key (ctrl-t) can be remapped via configuration file.
+
### 3.3 Configuration file
Options can be set via the configuration file first found in any of the
parity = none
stopbits = 1
color = 10
-dtr-pulse-duration = 50
[rpi3]
tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
no-autoconnect = enable
log = enable
log-file = rpi3.log
+line-pulse-duration = DTR=200,RTS=150
color = 12
[usb devices]
$ tio usb12
```
+Another configuration file example is available [here](example/tiorc).
## 4. Installation
-### 4.1 Installation using package manager
+### 4.1 Installation using package manager (Linux)
Packages for various GNU/Linux distributions are available. Please consult your
package manager tool to find and install tio.
If you would like to see tio included in your favorite distribution, please
reach out to their package maintainers team.
-### 4.2 Installation using snap
+### 4.2 Installation using snap (Linux)
Install latest stable version:
```
$ snap install tio
```
-Install bleeding edge:
+
+### 4.3 Installation using brew (MacOS, Linux)
+
+If you have [brew](http://brew.sh) installed:
+```
+ $ brew install tio
+```
+
+### 4.4 Installation using MacPorts (MacOS)
+
+If you have [MacPorts](https://www.macports.org) installed:
```
- $ snap install tio --edge
+ $ sudo port install tio
```
-### 4.3 Installation from source
+### 4.5 Installation from source
The latest source releases can be found [here](https://github.com/tio/tio/releases).
See meson\_options.txt for tio specific build options.
-Note: Please do no try to install from source if you are not familiar with
-how to build stuff using meson.
+Note: It is recommended to only try to install from source if you are familiar
+with how to build stuff using meson.
## 5. Contributing
- * Improve error/warning messaging when parsing configuration file
-
- * Refactor and consolidate arbitrary set speed function into one file for all platforms
-
- * Consider reworking line toggle and line pulse featueres in case requests are made to apply them to more lines:
-
- ctrl-t g Toggle serial line
-
- Which would then make tio ask which line to toggle:
-
- [20:50:11.691] Please enter which line number to toggle?
- [20:50:11.691] DTR(0) RTS(1) CTS(2) DSR(3) DCD(4) RI(5)
-
- ctrl-t p Pulse serial line
-
- [20:50:11.691] Please enter which line number to pulse?
- [20:50:11.691] DTR(0) RTS(1) CTS(2) DSR(3) DCD(4) RI(5)
-
- command-line:
- --line-pulse-duration="DTR=60,RTS=50,CTS=40,DSR=30,DCD=20,RI=10"
-
- config file:
- line-pulse-duration="DTR=60,RTS=50,CTS=40,DSR=30,DCD=20,RI=10"
+ * Split I/O feature
+
+ Allow to split input and output so that it is possible to manage these independently.
+
+ The general idea is to redirect the output stream on the socket port number
+ specified but then redirect the input stream on the same port number + 1.
+
+ Example:
+
+ tio /dev/ttyUSB0 --socket inet:4444,split-io
+
+ Will result in output stream being hosted on port 4444 and input stream hosted on port 4445.
+
+ For file sockets something similar can be arranged:
+
+ tio /dev/ttyUSB0 --socket unix:/tmp/tio-socket-0,split-io
+
+ Will result in output stream being hosted via /tmp/tio-socket-0 and input stream hosted via /tmp/tio-socket-0_input
--- /dev/null
+###############################
+# tio - https://tio.github.io #
+###############################
+
+# Example configuration file
+
+# Defaults
+baudrate = 115200
+databits = 8
+flow = none
+stopbits = 1
+parity = none
+prefix-ctrl-key = t
+output-delay = 0
+output-line-delay = 0
+no-autoconnect = disable
+hexadecimal = disable
+timestamp = disable
+color = bold
+
+# Sub-configuraions
+
+[rpi3]
+baudrate = 115200
+tty = /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A6009HU3-if00-port0
+socket = unix:/tmp/tio-socket-0
+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]
+baudrate = 9600
+tty = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0
+log = enable
+log-file = tincan.log
+log-strip = enable
+color = 11
+
+[usb devices]
+pattern = usb([0-9]*)
+tty = /dev/ttyUSB%s
+color = 12
.SH "DESCRIPTION"
.PP
-.B tio
-is a simple serial device tool which features a straightforward command-line
-and configuration file interface to easily connect to serial TTY devices for
-basic I/O operations.
+\fBtio\fR is a simple serial device tool which features a straightforward
+command-line and configuration file interface to easily connect to serial TTY
+devices for basic I/O operations.
.SH "OPTIONS"
Set stop bits (default: 1).
.TP
-.BR \-p ", " "\-\-parity odd" | even | none
+.BR \-p ", " "\-\-parity odd" | even | none | mark | space
Set parity (default: none).
+
+Note: With \fBmark\fR parity the parity bit is always 0. With \fBspace\fR
+parity the parity bit is always 1. Not all platforms support \fBmark\fR and
+\fBspace\fR parity.
+
.TP
.BR \-o ", " "\-\-output\-delay " \fI<ms>
Set output delay [ms] inserted between each sent character (default: 0).
.TP
-.BR " \-\-dtr\-pulse\-duration " \fI<ms>
+.BR \-O ", " "\-\-output\-line\-delay " \fI<ms>
+
+Set output delay [ms] inserted between each sent line (default: 0).
+
+.TP
+.BR " \-\-line\-pulse\-duration " \fI<duration>
+
+Set the pulse duration [ms] of each serial port line using the following key
+valur pair format in the duration field: <key>=<value>
+
+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.
-Set the duration [ms] of the DTR pulse (default: 100).
+The default pulse duration for each line is 100 ms.
+.RE
.TP
.BR \-n ", " \-\-no\-autoconnect
Disable automatic connect.
-By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect.
+By default tio automatically connects to the provided device if present. If the
+device is not present, it will wait for it to appear and then connect. If the
+connection is lost (eg. device disconnects), it will wait for the device to
+reappear and then reconnect.
-However, if the
-.B \-\-no\-autoconnect
-option is provided, tio will exit if the device is not present or an established connection is lost.
+However, if the \fB\-\-no\-autoconnect\fR option is provided, tio will exit if
+the device is not present or an established connection is lost.
.TP
.BR \-e ", " "\-\-local\-echo
.IP "\fBiso8601"
ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss")
.PP
-Default format is
-.B 24hour
+Default format is \fB24hour\fR
.RE
.TP
.TP
.BR \-l ", " \-\-log
-Enable log to file. If no filename is provided the filename will be automatically generated.
+Enable log to file.
+
+If no filename is provided the filename will be
+automatically generated.
.TP
.BR " \-\-log-file \fI<filename>
.TP
.BR \-m ", " "\-\-map " \fI<flags>
-Map (replace, translate) special characters on input or output. The following mapping flags are supported:
+Map (replace, translate) characters on input or output. The following mapping
+flags are supported:
+
.RS
.TP 12n
.IP "\fBICRNL"
-Map CR to NL on input (unless IGNCR is set).
+Map CR to NL on input (unless IGNCR is set)
.IP "\fBIGNCR"
-Ignore CR on input.
+Ignore CR on input
.IP "\fBINLCR"
-Map NL to CR on input.
+Map NL to CR on input
.IP "\fBINLCRNL"
-Map NL to CR-NL on input.
+Map NL to CR-NL on input
.IP "\fBOCRNL"
-Map CR to NL on output.
+Map CR to NL on output
.IP "\fBODELBS"
-Map DEL to BS on output.
+Map DEL to BS on output
.IP "\fBONLCRNL"
-Map NL to CR-NL on output.
+Map NL to CR-NL on output
+.IP "\fBOLTU"
+Map lowercase characters to uppercase on output
.P
If defining more than one flag, the flags must be comma separated.
.RE
Enable hexadecimal mode.
.TP
-.BR \-c ", " "\-\-color " \fI0..255|none|list
+.BR \-c ", " "\-\-color " \fI0..255|bold|none|list
-Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color.
+Colorize tio text using ANSI color code value ranging from 0 to 255 or use
+"none" for no color or use "bold" to apply bold formatting to existing system
+color.
Use "list" to print a list of available ANSI color codes.
-Default value is 15.
+Default value is "bold".
.TP
.BR \-S ", " "\-\-socket \fI<socket>\fR\fB
-Redirect I/O to socket. Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that
-.B ctrl-t
-sequences are not recognized), and any input from the serial port is multiplexed to the terminal and all connected clients.
+Redirect I/O to socket. Any input from clients connected to the socket is sent
+on the serial port as if entered at the terminal where tio is running (except
+that \fBctrl-t\fR sequences are not recognized), and any input from the serial
+port is multiplexed to the terminal and all connected clients.
Sockets remain open while the serial port is disconnected, and writes will block.
.SH "KEYS"
.PP
.TP 16n
-In session, the following key sequences are intercepted as tio commands:
+In session, the following key sequences, a prefix key (default: ctrl-t) followed by a command key, are intercepted as tio commands:
.IP "\fBctrl-t ?"
List available key commands
.IP "\fBctrl-t b"
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 t"
-Send ctrl-t key code
-.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
+Toggle line timestamp mode
+.IP "\fBctrl-t U"
+Toggle conversion to uppercase on output
.IP "\fBctrl-t v"
Show version
.SH "HEXADECIMAL MODE"
-.TP
+.PP
In hexadecimal mode each incoming byte is printed out as a hexadecimal value.
-.TP
-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 \fI0A\fR.
+
+.PP
+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
+\fI0A\fR.
.SH "CONFIGURATION FILE"
.PP
-.TP 16n
-Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed:
+Options can be set via configuration file using the INI format. \fBtio\fR uses
+the configuration file first found in the following locations in the order
+listed:
+
.PP
.I $XDG_CONFIG_HOME/tio/tiorc
.PP
.PP
.I $HOME/.tiorc
-.TP
-Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio.
+.PP
+Labels can be used to group settings into named sub-configurations which can be
+activated from the command-line when starting tio.
-.TP
-.TP
-tio will try to match the user input to a sub-configuration by name or by pattern to get the tty and other options.
+.PP
+\fBtio\fR will try to match the user input to a sub-configuration by name or by
+pattern to get the tty and other options.
-.TP
+.PP
Options without any label change the default options.
-.TP
+.PP
Any options set via command-line will override options set in the configuration file.
-.TP
+.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"
.IP "\fBparity"
Set parity
.IP "\fBoutput-delay"
-Set output delay
-.IP "\fBdtr-pulse-duration"
-Set DTR pulse duration
+Set output character delay
+.IP "\fBoutput-line-delay"
+Set output line delay
+.IP "\fBline-pulse-duration"
+Set line pulse duration
.IP "\fBno-autoconnect"
Disable automatic connect
.IP "\fBlog"
.IP "\fBtimestamp-format"
Set timestamp format
.IP "\fBmap"
-Map special characters on input or output
+Map characters on input or output
.IP "\fBcolor"
Colorize tio text using ANSI color code ranging from 0 to 255
.IP "\fBhexadecimal"
Enable hexadecimal mode
.IP "\fBsocket"
Set socket to redirect I/O to
+.IP "\fBprefix-ctrl-key"
+Set prefix ctrl key (a..z, default: t)
.SH "CONFIGURATION FILE EXAMPLES"
parity = none
stopbits = 1
color = 10
-dtr-pulse-duration = 50
+line-pulse-duration = DTR=200,RTS=400
.ec
.fi
.RE
$ nc -N 10.0.0.42 4444
.TP
-Pipe data from file to the serial device:
+Pipe command to the serial device:
+
+$ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
+
+.TP
+Likewise, to pipe data from file to the serial device:
$ cat data.bin | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
--- /dev/null
+tio(1) User Commands tio(1)
+
+NAME
+ tio - a simple serial device I/O tool
+
+SYNOPSIS
+ tio [<options>] <tty-device|sub-config>
+
+DESCRIPTION
+ tio is a simple serial device tool which features a straightforward command-line and configuration file interface to easily con‐
+ nect to serial TTY devices for basic I/O operations.
+
+OPTIONS
+ -b, --baudrate <bps>
+
+ Set baud rate [bps] (default: 115200).
+
+ -d, --databits 5|6|7|8
+
+ Set data bits (default: 8).
+
+ -f, --flow hard|soft|none
+
+ Set flow control (default: none).
+
+ -s, --stopbits 1|2
+
+ Set stop bits (default: 1).
+
+ -p, --parity odd|even|none|mark|space
+
+ Set parity (default: none).
+
+ Note: With mark parity the parity bit is always 0. With space parity the parity bit is always 1. Not all platforms support
+ mark and space parity.
+
+ -o, --output-delay <ms>
+
+ Set output delay [ms] inserted between each sent character (default: 0).
+
+ -O, --output-line-delay <ms>
+
+ Set output delay [ms] inserted between each sent line (default: 0).
+
+ --line-pulse-duration <duration>
+
+ Set the pulse duration [ms] of each serial port line using the following key valur pair format in the duration field:
+ <key>=<value>
+
+ Each key represents a serial line. The following keys are available:
+
+ DTR - Data Terminal Ready
+
+ RTS - Request To Send
+
+ CTS - Clear To Send
+
+ DSR - Data Set Ready
+
+ DCD - Data Carrier Detect
+
+ RI - Ring Indicator
+
+ If defining more than one key value pair, the pairs must be comma separated.
+
+ The default pulse duration for each line is 100 ms.
+
+ -n, --no-autoconnect
+
+ Disable automatic connect.
+
+ By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it
+ to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and
+ then reconnect.
+
+ However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connec‐
+ tion is lost.
+
+ -e, --local-echo
+
+ Enable local echo.
+
+ -t, --timestamp
+
+ Enable line timestamp.
+
+ --timestamp-format <format>
+
+ Set timestamp format to any of the following timestamp formats:
+
+ 24hour 24-hour format ("hh:mm:ss.sss")
+
+ 24hour-start 24-hour format relative to start time
+
+ 24hour-delta 24-hour format relative to previous timestamp
+
+ iso8601 ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss")
+
+ Default format is 24hour
+
+ -L, --list-devices
+
+ List available serial devices.
+
+ -l, --log
+
+ Enable log to file.
+
+ If no filename is provided the filename will be automatically generated.
+
+ --log-file <filename>
+
+ Set log filename.
+
+ --log-strip
+
+ Strip control characters and escape sequences from log.
+
+ -m, --map <flags>
+
+ Map (replace, translate) characters on input or output. The following mapping flags are supported:
+
+ ICRNL Map CR to NL on input (unless IGNCR is set)
+
+ IGNCR Ignore CR on input
+
+ INLCR Map NL to CR on input
+
+ INLCRNL Map NL to CR-NL on input
+
+ OCRNL Map CR to NL on output
+
+ ODELBS Map DEL to BS on output
+
+ ONLCRNL Map NL to CR-NL on output
+
+ OLTU Map lowercase characters to uppercase on output
+
+ If defining more than one flag, the flags must be comma separated.
+
+ -x, --hexadecimal
+
+ Enable hexadecimal mode.
+
+ -c, --color 0..255|bold|none|list
+
+ Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color or use "bold" to apply bold
+ formatting to existing system color.
+
+ Use "list" to print a list of available ANSI color codes.
+
+ Default value is "bold".
+
+ -S, --socket <socket>
+
+ Redirect I/O to socket. Any input from clients connected to the socket is sent on the serial port as if entered at the ter‐
+ minal where tio is running (except that ctrl-t sequences are not recognized), and any input from the serial port is multi‐
+ plexed to the terminal and all connected clients.
+
+ Sockets remain open while the serial port is disconnected, and writes will block.
+
+ Various socket types are supported using the following prefixes in the socket field:
+
+ unix:<filename> Unix Domain Socket (file)
+
+ inet:<port> Internet Socket (network)
+
+ inet6:<port> Internet IPv6 Socket (network)
+
+ If port is 0 or no port is provided default port 3333 is used.
+
+ At present there is a hardcoded limit of 16 clients connected at one time.
+
+ -v, --version
+
+ Display program version.
+
+ -h, --help
+
+ Display help.
+
+KEYS
+ In session, the following key sequences, a prefix key (default: ctrl-t) followed by a command key, are intercepted as tio com‐
+ mands:
+
+ ctrl-t ? List available key commands
+
+ ctrl-t b Send serial break (triggers SysRq on Linux, etc.)
+
+ ctrl-t c Show configuration (baudrate, databits, etc.)
+
+ ctrl-t e Toggle local echo mode
+
+ ctrl-t g Toggle serial port line
+
+ ctrl-t h Toggle hexadecimal mode
+
+ ctrl-t l Clear screen
+
+ ctrl-t L Show line states (DTR, RTS, CTS, DSR, DCD, RI)
+
+ ctrl-t p Pulse serial port line
+
+ ctrl-t q Quit
+
+ ctrl-t s Show TX/RX statistics
+
+ ctrl-t t Toggle line timestamp mode
+
+ ctrl-t U Toggle conversion to uppercase on output
+
+ ctrl-t v Show version
+
+HEXADECIMAL MODE
+ In hexadecimal mode each incoming byte is printed out as a hexadecimal value.
+
+ Bytes can be sent in this mode by typing the two-character hexadecimal representation of the value, e.g.: to send 0xA you must
+ type 0a or 0A.
+
+CONFIGURATION FILE
+ Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following loca‐
+ tions in the order listed:
+
+ $XDG_CONFIG_HOME/tio/tiorc
+
+ $HOME/.config/tio/tiorc
+
+ $HOME/.tiorc
+
+ Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio.
+
+ tio will try to match the user input to a sub-configuration by name or by pattern to get the tty and other options.
+
+ Options without any label change the default options.
+
+ Any options set via command-line will override options set in the configuration file.
+
+ The following configuration file options are available:
+
+ pattern Pattern matching user input. This pattern can be an extended regular expression with a single group.
+
+ tty tty device to open. If it contains a "%s" it is substituted with the first group match.
+
+ baudrate Set baud rate
+
+ databits Set data bits
+
+ flow Set flow control
+
+ stopbits Set stop bits
+
+ parity Set parity
+
+ output-delay Set output character delay
+
+ output-line-delay Set output line delay
+
+ line-pulse-duration Set line pulse duration
+
+ no-autoconnect Disable automatic connect
+
+ log Enable log to file
+
+ log-file Set log filename
+
+ log-strip Enable strip of control and escape sequences from log
+
+ local-echo Enable local echo
+
+ timestamp Enable line timestamp
+
+ timestamp-format Set timestamp format
+
+ map Map characters on input or output
+
+ color Colorize tio text using ANSI color code ranging from 0 to 255
+
+ hexadecimal Enable hexadecimal mode
+
+ socket Set socket to redirect I/O to
+
+ prefix-ctrl-key Set prefix ctrl key (a..z, default: t)
+
+CONFIGURATION FILE EXAMPLES
+ To change the default configuration simply set options like so:
+
+ # Defaults
+ baudrate = 9600
+ databits = 8
+ parity = none
+ stopbits = 1
+ color = 10
+ line-pulse-duration = DTR=200,RTS=400
+
+ Named sub-configurations can be added via labels:
+
+ [rpi3]
+ tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+ baudrate = 115200
+ color = 11
+
+ Activate the sub-configuration by name:
+
+ $ tio rpi3
+
+ Which is equivalent to:
+
+ $ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+
+ A sub-configuration can also be activated by its pattern which supports regular expressions:
+
+ [usb device]
+ pattern = usb([0-9]*)
+ tty = /dev/ttyUSB%s
+ baudrate = 115200
+
+ Activate the sub-configuration by pattern match:
+
+ $ tio usb12
+
+ Which is equivalent to:
+
+ $ tio -b 115200 /dev/ttyUSB12
+
+ It is also possible to combine use of sub-configuration and command-line options. For example:
+
+ $ tio -l -t usb12
+
+EXAMPLES
+ Typical use is without options:
+
+ $ tio /dev/ttyUSB0
+
+ Which corresponds to the commonly used default options:
+
+ $ tio -b 115200 -d 8 -f none -s 1 -p none /dev/ttyUSB0
+
+ It is recommended to connect serial tty devices by ID:
+
+ $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+
+ Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then
+ reconnected.
+
+ Redirect serial device I/O to Unix file socket for scripting:
+
+ $ tio -S unix:/tmp/tmux-socket0 /dev/ttyUSB0
+
+ Then, to issue a command via the file socket simply do:
+
+ $ echo "ls -la" | nc -UN /tmp/tmux-socket0 > /dev/null
+
+ Or use the expect command to script an interaction:
+
+ #!/usr/bin/expect -f
+
+ set timeout -1
+ log_user 0
+
+ spawn nc -UN /tmp/tio-socket0
+ set uart $spawn_id
+
+ send -i $uart "date\n"
+ expect -i $uart "prompt> "
+ send -i $uart "ls -la\n"
+ expect -i $uart "prompt> "
+
+ Redirect device I/O to network file socket for remote tty sharing:
+
+ $ tio --socket inet:4444 /dev/ttyUSB0
+
+ Then, use netcat to connect to the shared tty session over network (assuming tio is hosted on IP 10.0.0.42):
+
+ $ nc -N 10.0.0.42 4444
+
+ Pipe command to the serial device:
+
+ $ echo "ls -la" | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+
+ Likewise, to pipe data from file to the serial device:
+
+ $ cat data.bin | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
+
+WEBSITE
+ Visit https://tio.github.io
+
+AUTHOR
+ Created by Martin Lund <martin.lund@keep-it-simple.com>.
+
+tio 1.46 2022-07-15 tio(1)
project('tio', 'c',
- version : '1.43',
+ version : '1.47',
license : [ 'GPL-2'],
meson_version : '>= 0.53.2',
default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ]
)
# The tag date of the project_version(), update when the version bumps.
-version_date = '2022-07-09'
+version_date = '2022-07-23'
# Test for dynamic baudrate configuration interface
compiler = meson.get_compiler('c')
-s --stopbits \
-p --parity \
-o --output-delay \
- --dtr-pulse-duration \
+ -o --output-line-delay \
+ --line-pulse-duration \
-n --no-autoconnect \
-e --local-echo \
-l --log \
return 0
;;
-o | --output-delay)
- COMPREPLY=( $(compgen -W "0 1 10 100" -- ${cur}) )
+ COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) )
return 0
;;
- --dtr-pulse-duration)
- COMPREPLY=( $(compgen -W "10 50 100 500" -- ${cur}) )
+ -O | --output-line-delay)
+ COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) )
+ return 0
+ ;;
+ --line-pulse-duration)
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-n | --no-autoconnect)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
- -l | --log-file)
+ --log-file)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
- -l | --log-strip)
+ --log-strip)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
if (ret)
{
regerror(ret, &re, err, sizeof(err));
- fprintf(stderr, "regex error: %s", err);
+ tio_error_printf("Regex failure: %s", err);
return ret;
}
{
option.output_delay = atoi(value);
}
- else if (!strcmp(name, "dtr-pulse-duration"))
+ else if (!strcmp(name, "output-line-delay"))
{
- option.dtr_pulse_duration = atoi(value);
+ option.output_line_delay = atoi(value);
+ }
+ else if (!strcmp(name, "line-pulse-duration"))
+ {
+ line_pulse_duration_option_parse(value);
}
else if (!strcmp(name, "no-autoconnect"))
{
// Ignore
return 0;
}
-
- if (!strcmp(value, "none"))
+ else if (!strcmp(value, "none"))
{
option.color = -1; // No color
return 0;
}
+ else if (!strcmp(value, "bold"))
+ {
+ option.color = 256; // Bold
+ return 0;
+ }
option.color = atoi(value);
if ((option.color < 0) || (option.color > 255))
asprintf(&c->socket, "%s", value);
option.socket = c->socket;
}
+ else if (!strcmp(name, "prefix-ctrl-key"))
+ {
+ if (ctrl_key_code(value[0]) > 0)
+ {
+ option.prefix_code = ctrl_key_code(value[0]);
+ option.prefix_key = value[0];
+ }
+ }
+
}
return 0;
}
c = malloc(sizeof(struct config_t));
if (!c)
{
- fprintf(stderr, "Error: Insufficient memory allocation");
+ tio_error_printf("Insufficient memory allocation");
exit(EXIT_FAILURE);
}
memset(c, 0, sizeof(struct config_t));
ret = ini_parse(c->path, data_handler, NULL);
if (ret < 0)
{
- fprintf(stderr, "Error: Unable to parse configuration file (%d)", ret);
+ tio_error_printf("Unable to parse configuration file (%d)", ret);
exit(EXIT_FAILURE);
}
free(c->section_name);
ret = ini_parse(c->path, section_name_search_handler, NULL);
if (!c->section_name)
{
- debug_printf("Unable to match user input to configuration section (%d)", ret);
+ tio_debug_printf("Unable to match user input to configuration section (%d)", ret);
return;
}
}
ret = ini_parse(c->path, data_handler, NULL);
if (ret < 0)
{
- fprintf(stderr, "Error: Unable to parse configuration file (%d)", ret);
+ tio_error_printf("Unable to parse configuration file (%d)", ret);
exit(EXIT_FAILURE);
}
* 02110-1301, USA.
*/
+#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf
+
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include "options.h"
#include "print.h"
#include "error.h"
-char error[2][1000];
+static char error[2][1000];
+static bool in_session = false;
+
+void error_enter_session_mode(void)
+{
+ in_session = true;
+}
+
+void error_printf_(const char *format, ...)
+{
+ va_list args;
+ char *line;
+
+ va_start(args, format);
+ vasprintf(&line, format, args);
+
+ if (in_session)
+ {
+ if (print_tainted)
+ {
+ putchar('\n');
+ }
+ ansi_error_printf("[%s] %s", current_time(), line);
+ }
+ else
+ {
+ fprintf(stderr, "%s\n", line);
+ }
+
+ va_end(args);
+
+ print_tainted = false;
+ free(line);
+}
+
+void tio_error_printf(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(error[0], 1000, format, args);
+ va_end(args);
+}
+
+void tio_error_printf_silent(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(error[1], 1000, format, args);
+ va_end(args);
+}
void error_exit(void)
{
if (error[0][0] != 0)
{
/* Print error */
- tio_error_printf("Error: %s", error[0]);
+ error_printf_("Error: %s", error[0]);
}
else if ((error[1][0] != 0) && (option.no_autoconnect))
{
/* Print silent error */
- tio_error_printf("Error: %s", error[1]);
+ error_printf_("Error: %s", error[1]);
}
}
#define TIO_SUCCESS 0
#define TIO_ERROR 1
-extern char error[2][1000];
-
+void tio_error_printf(const char *format, ...);
+void tio_error_printf_silent(const char *format, ...);
void error_exit(void);
+void error_enter_session_mode(void);
+++ /dev/null
-/*
- * tio - a simple serial terminal I/O tool
- *
- * Copyright (c) 2017 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 <sys/ioctl.h>
-#include <IOKit/serial/ioss.h>
-
-int iossiospeed(int fd, int baudrate)
-{
- return ioctl(fd, IOSSIOSPEED, (char *)&baudrate);
-}
exit(EXIT_FAILURE);
}
- // Enable full buffering
- setvbuf(fp, file_buffer, _IOFBF, BUFSIZ);
+ // Enable line buffering
+ setvbuf(fp, file_buffer, _IOLBF, BUFSIZ);
}
bool log_strip(char c)
if (log_error)
{
- error_printf("Could not open log file %s (%s)", option.log_filename, strerror(errno));
+ tio_error_printf("Could not open log file %s (%s)", option.log_filename, strerror(errno));
}
else if (option.log)
{
/* Initialize ANSI text formatting (colors etc.) */
print_init_ansi_formatting();
+ /* Change error printing mode */
+ error_enter_session_mode();
+
/* Print launch hints */
tio_printf("tio v%s", VERSION);
if (interactive_mode)
{
- tio_printf("Press ctrl-t q to quit");
+ tio_printf("Press ctrl-%c q to quit", option.prefix_key);
+ } else
+ {
+ tio_printf("Non-interactive mode enabled");
+ tio_printf("Press ctrl-c to quit");
}
/* Open socket */
}
/* Connect to tty device */
- if ((option.no_autoconnect) || (!interactive_mode))
+ if (option.no_autoconnect)
{
status = tty_connect();
}
'print.c',
'configfile.c',
'signals.c',
- 'socket.c'
+ 'socket.c',
+ 'setspeed.c'
]
tio_dep = dependency('inih', required: true,
tio_c_args = ['-Wno-unused-result']
if enable_setspeed2
- tio_sources += 'setspeed2.c'
tio_c_args += '-DHAVE_TERMIOS2'
endif
if enable_iossiospeed
- tio_sources += 'iossiospeed.c'
tio_c_args += '-DHAVE_IOSSIOSPEED'
endif
case TIMESTAMP_24HOUR_START:
// "hh:mm:ss.sss" (24 hour format relative to start time)
timersub(&tv_now, &tv_start, &tv);
- tv.tv_sec -= 3600; // Why is this needed??
- tm = localtime(&tv.tv_sec);
+ tm = gmtime(&tv.tv_sec);
len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm);
break;
case TIMESTAMP_24HOUR_DELTA:
// "hh:mm:ss.sss" (24 hour format relative to previous time stamp)
timersub(&tv_now, &tv_previous, &tv);
- tv.tv_sec -= 3600; // Why is this needed??
- tm = localtime(&tv.tv_sec);
+ tm = gmtime(&tv.tv_sec);
len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm);
break;
case TIMESTAMP_ISO8601:
{
struct timespec ts;
+ if (ms <= 0)
+ {
+ return;
+ }
+
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000;
return result;
}
+
+int ctrl_key_code(unsigned char key)
+{
+ if ((key >= 'a') && (key <= 'z'))
+ {
+ return key & ~0x60;
+ }
+
+ return -1;
+}
char * current_time(void);
void delay(long ms);
long string_to_long(char *string);
+int ctrl_key_code(unsigned char key);
OPT_TIMESTAMP_FORMAT,
OPT_LOG_FILE,
OPT_LOG_STRIP,
- OPT_DTR_PULSE_DURATION,
+ OPT_LINE_PULSE_DURATION,
};
/* Default options */
.stopbits = 1,
.parity = "none",
.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,
.timestamp = TIMESTAMP_NONE,
.socket = NULL,
.map = "",
- .color = 15,
+ .color = 256, // Bold
.hex_mode = false,
+ .prefix_code = 20, // ctrl-t
+ .prefix_key = 't',
};
void print_help(char *argv[])
printf("Connect to tty device directly or via sub-configuration.\n");
printf("\n");
printf("Options:\n");
- printf(" -b, --baudrate <bps> Baud rate (default: 115200)\n");
- printf(" -d, --databits 5|6|7|8 Data bits (default: 8)\n");
- printf(" -f, --flow hard|soft|none Flow control (default: none)\n");
- printf(" -s, --stopbits 1|2 Stop bits (default: 1)\n");
- printf(" -p, --parity odd|even|none Parity (default: none)\n");
- printf(" -o, --output-delay <ms> Output delay (default: 0)\n");
- printf(" --dtr-pulse-duration <ms> DTR pulse duration (default: 100)\n");
- printf(" -n, --no-autoconnect Disable automatic connect\n");
- printf(" -e, --local-echo Enable local echo\n");
- printf(" -t, --timestamp Enable line timestamp\n");
- printf(" --timestamp-format <format> Set timestamp format (default: 24hour)\n");
- printf(" -L, --list-devices List available serial devices\n");
- printf(" -l, --log Enable log to file\n");
- printf(" --log-file <filename> Set log filename\n");
- printf(" --log-strip Strip control characters and escape sequences\n");
- printf(" -m, --map <flags> Map special characters\n");
- printf(" -c, --color 0..255|none|list Colorize tio text (default: 15)\n");
- printf(" -S, --socket <socket> Redirect I/O to file or network socket\n");
- printf(" -x, --hexadecimal Enable hexadecimal mode\n");
- printf(" -v, --version Display version\n");
- printf(" -h, --help Display help\n");
+ printf(" -b, --baudrate <bps> Baud rate (default: 115200)\n");
+ printf(" -d, --databits 5|6|7|8 Data bits (default: 8)\n");
+ printf(" -f, --flow hard|soft|none Flow control (default: none)\n");
+ printf(" -s, --stopbits 1|2 Stop bits (default: 1)\n");
+ printf(" -p, --parity odd|even|none|mark|space Parity (default: none)\n");
+ printf(" -o, --output-delay <ms> Output character delay (default: 0)\n");
+ printf(" -O, --output-line-delay <ms> Output line delay (default: 0)\n");
+ printf(" --line-pulse-duration <duration> Set 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");
+ printf(" --timestamp-format <format> Set timestamp format (default: 24hour)\n");
+ printf(" -L, --list-devices List available serial devices\n");
+ printf(" -l, --log Enable log to file\n");
+ printf(" --log-file <filename> Set log filename\n");
+ printf(" --log-strip Strip control characters and escape sequences\n");
+ printf(" -m, --map <flags> Map characters\n");
+ printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n");
+ printf(" -S, --socket <socket> Redirect I/O to file or network socket\n");
+ printf(" -x, --hexadecimal Enable hexadecimal mode\n");
+ printf(" -v, --version Display version\n");
+ printf(" -h, --help Display help\n");
printf("\n");
printf("Options and sub-configurations may be set via configuration file.\n");
printf("\n");
- printf("In session, press ctrl-t q to quit.\n");
- printf("\n");
printf("See the man page for more details.\n");
}
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);
tio_printf(" Local echo: %s", option.local_echo ? "enabled" : "disabled");
tio_printf(" Timestamp: %s", timestamp_state_to_string(option.timestamp));
tio_printf(" Output delay: %d", option.output_delay);
+ 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)
{
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' },
- {"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 */
int option_index = 0;
/* Parse argument using getopt_long */
- c = getopt_long(argc, argv, "b:d:f:s:p:o:netLlS:m:c:xvh", long_options, &option_index);
+ c = getopt_long(argc, argv, "b:d:f:s:p:o:O:netLlS:m:c:xvh", long_options, &option_index);
/* Detect the end of the options */
if (c == -1)
option.output_delay = string_to_long(optarg);
break;
- case OPT_DTR_PULSE_DURATION:
- option.dtr_pulse_duration = string_to_long(optarg);
+ case 'O':
+ option.output_line_delay = string_to_long(optarg);
+ break;
+
+ case OPT_LINE_PULSE_DURATION:
+ line_pulse_duration_option_parse(optarg);
break;
case 'n':
break;
case OPT_LOG_FILE:
+ option.log = true;
option.log_filename = optarg;
break;
}
exit(EXIT_SUCCESS);
}
-
- if (!strcmp(optarg, "none"))
+ else if (!strcmp(optarg, "none"))
+ {
+ option.color = -1; // No color
+ break;
+ }
+ else if (!strcmp(optarg, "bold"))
{
- option.color = -1;
+ option.color = 256; // Bold
break;
}
option.color = string_to_long(optarg);
if ((option.color < 0) || (option.color > 255))
{
- printf("Error: Invalid color code\n");
+ tio_error_printf("Invalid color code");
exit(EXIT_FAILURE);
}
break;
/* Assume first non-option is the tty device name */
if (strcmp(option.tty_device, ""))
- optind++;
+ optind++;
else if (optind < argc)
option.tty_device = argv[optind++];
if (strlen(option.tty_device) == 0)
{
- printf("Error: Missing tty device or sub-configuration name\n");
+ tio_error_printf("Missing tty device or sub-configuration name");
exit(EXIT_FAILURE);
}
/* Print any remaining command line arguments (unknown options) */
if (optind < argc)
{
- printf("Error: Unknown argument ");
+ fprintf(stderr, "Error: Unknown argument ");
while (optind < argc)
printf("%s ", argv[optind++]);
- printf("\n");
+ fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
}
int stopbits;
char *parity;
int output_delay;
- int dtr_pulse_duration;
+ int output_line_delay;
+ 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;
const char *socket;
int color;
bool hex_mode;
+ unsigned char prefix_code;
+ unsigned char prefix_key;
};
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);
void print_init_ansi_formatting()
{
- // Set bold text with user defined ANSI color
- sprintf(ansi_format, "\e[1;38;5;%dm", option.color);
+ if (option.color == 256)
+ {
+ // Set bold text with no color changes
+ sprintf(ansi_format, "\e[1m");
+ }
+ else
+ {
+ // Set bold text with user defined ANSI color
+ sprintf(ansi_format, "\e[1;38;5;%dm", option.color);
+ }
}
#define ansi_error_printf(format, args...) \
{ \
if (option.color < 0) \
- fprintf (stdout, "\r" format "\r\n", ## args); \
+ fprintf (stderr, "\r" format "\r\n", ## args); \
else \
fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
fflush(stderr); \
fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \
}
-#define warning_printf(format, args...) \
+#define tio_warning_printf(format, args...) \
{ \
if (print_tainted) \
putchar('\n'); \
print_tainted = false; \
}
-#define tio_error_printf(format, args...) \
-{ \
- if (print_tainted) \
- putchar('\n'); \
- ansi_error_printf("[%s] " format, current_time(), ## args); \
- print_tainted = false; \
-}
-
-#define error_printf(format, args...) \
- snprintf(error[0], 1000, format, ## args);
-
-#define error_printf_silent(format, args...) \
- snprintf(error[1], 1000, format, ## args);
-
#ifdef DEBUG
-#define debug_printf(format, args...) \
+#define tio_debug_printf(format, args...) \
fprintf (stdout, "[debug] " format, ## args)
-#define debug_printf_raw(format, args...) \
+#define tio_debug_printf_raw(format, args...) \
fprintf (stdout, "" format, ## args)
#else
-#define debug_printf(format, args...)
-#define debug_printf_raw(format, args...)
+#define tio_debug_printf(format, args...)
+#define tio_debug_printf_raw(format, args...)
#endif
void print_hex(char c);
--- /dev/null
+/*
+ * tio - a simple serial terminal I/O tool
+ *
+ * Copyright (c) 2017-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
+ * 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 <errno.h>
+
+#ifdef HAVE_TERMIOS2
+#define termios asmtermios
+#include <sys/ioctl.h>
+#undef termios
+#include <asm-generic/ioctls.h>
+#include <asm-generic/termbits.h>
+
+#elif HAVE_IOSSIOSPEED
+#include <sys/ioctl.h>
+#include <IOKit/serial/ioss.h>
+#endif
+
+
+#ifdef HAVE_TERMIOS2
+int setspeed(int fd, int baudrate)
+{
+ struct termios2 tio;
+ int status;
+
+ status = ioctl(fd, TCGETS2, &tio);
+
+ // Set baudrate speed using termios2 interface
+ tio.c_cflag &= ~CBAUD;
+ tio.c_cflag |= BOTHER;
+ tio.c_ispeed = baudrate;
+ tio.c_ospeed = baudrate;
+
+ status = ioctl(fd, TCSETS2, &tio);
+
+ return status;
+}
+
+#elif HAVE_IOSSIOSPEED
+int setspeed(int fd, int baudrate)
+{
+ return ioctl(fd, IOSSIOSPEED, (char *)&baudrate);
+}
+
+#else
+int setspeed(int fd, int baudrate)
+{
+ errno = EINVAL;
+ return -1;
+}
+#endif
--- /dev/null
+/*
+ * tio - a simple serial terminal I/O tool
+ *
+ * Copyright (c) 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
+ * 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
+
+int setspeed(int fd, int baudrate);
+++ /dev/null
-/*
- * tio - a simple serial terminal I/O tool
- *
- * Copyright (c) 2017-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
- * 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.
- */
-
-#define termios asmtermios
-#include <sys/ioctl.h>
-#undef termios
-#include <asm-generic/ioctls.h>
-#include <asm-generic/termbits.h>
-
-int setspeed2(int fd, int baudrate)
-{
- struct termios2 tio;
- int status;
-
- status = ioctl(fd, TCGETS2, &tio);
-
- // Set baudrate speed using termios2 interface
- tio.c_cflag &= ~CBAUD;
- tio.c_cflag |= BOTHER;
- tio.c_ispeed = baudrate;
- tio.c_ospeed = baudrate;
-
- status = ioctl(fd, TCSETS2, &tio);
-
- return status;
-}
#include "error.h"
#include "print.h"
#include "misc.h"
+#include "tty.h"
static void signal_handler(int signum)
{
- UNUSED(signum);
- tio_printf("Received hangup signal!");
+ switch (signum)
+ {
+ case SIGHUP:
+ tio_printf("Received SIGHUP signal!");
+ break;
+ case SIGINT:
+ tio_printf("Received SIGINT signal!");
+ break;
+ }
exit(EXIT_FAILURE);
}
void signal_handlers_install(void)
{
signal(SIGHUP, signal_handler);
+ signal(SIGINT, signal_handler);
}
if (strlen(socket_filename()) == 0)
{
- error_printf("Missing socket filename");
+ tio_error_printf("Missing socket filename");
exit(EXIT_FAILURE);
}
if (strlen(socket_filename()) > sizeof(sockaddr_unix.sun_path) - 1)
{
- error_printf("Socket file path %s too long", option.socket);
+ tio_error_printf("Socket file path %s too long", option.socket);
exit(EXIT_FAILURE);
}
}
if (port_number < 0)
{
- error_printf("Invalid port number: %d", port_number);
+ tio_error_printf("Invalid port number: %d", port_number);
exit(EXIT_FAILURE);
}
}
if (port_number < 0)
{
- error_printf("Invalid port number: %d", port_number);
+ tio_error_printf("Invalid port number: %d", port_number);
exit(EXIT_FAILURE);
}
}
if (socket_family == AF_UNSPEC)
{
- error_printf("%s: Invalid socket scheme, must be prefixed with 'unix:', 'inet:', or 'inet6:'", option.socket);
+ tio_error_printf("%s: Invalid socket scheme, must be prefixed with 'unix:', 'inet:', or 'inet6:'", option.socket);
exit(EXIT_FAILURE);
}
break;
default:
- error_printf("Invalid socket family (%d)", socket_family);
+ tio_error_printf("Invalid socket family (%d)", socket_family);
exit(EXIT_FAILURE);
break;
}
sockfd = socket(socket_family, SOCK_STREAM, 0);
if (sockfd < 0)
{
- error_printf("Failed to create socket (%s)", strerror(errno));
+ tio_error_printf("Failed to create socket (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
/* Bind */
if (bind(sockfd, sockaddr_p, socklen) < 0)
{
- error_printf("Failed to bind to socket (%s)", strerror(errno));
+ tio_error_printf("Failed to bind to socket (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
/* Listen */
if (listen(sockfd, MAX_SOCKET_CLIENTS) < 0)
{
- error_printf("Failed to listen on socket (%s)", strerror(errno));
+ tio_error_printf("Failed to listen on socket (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
{
if (write(clientfds[i], &input_char, 1) <= 0)
{
- error_printf_silent("Failed to write to socket (%s)", strerror(errno));
+ tio_error_printf_silent("Failed to write to socket (%s)", strerror(errno));
close(clientfds[i]);
clientfds[i] = -1;
}
}
if (status < 0)
{
- error_printf_silent("Failed to read from socket (%s)", strerror(errno));
+ tio_error_printf_silent("Failed to read from socket (%s)", strerror(errno));
close(clientfds[i]);
clientfds[i] = -1;
continue;
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include "log.h"
#include "error.h"
#include "socket.h"
-
-#ifdef HAVE_TERMIOS2
-extern int setspeed2(int fd, int baudrate);
-#endif
-
-#ifdef HAVE_IOSSIOSPEED
-extern int iossiospeed(int fd, int baudrate);
-#endif
+#include "setspeed.h"
#ifdef __APPLE__
#define PATH_SERIAL_DEVICES "/dev/"
#define PATH_SERIAL_DEVICES "/dev/serial/by-id/"
#endif
+#ifndef CMSPAR
+#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_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;
static bool map_o_cr_nl = false;
static bool map_o_nl_crnl = false;
static bool map_o_del_bs = false;
+static bool map_o_ltu = false;
static char hex_chars[2];
static unsigned char hex_char_index = 0;
static char tty_buffer[BUFSIZ*2];
}
}
-void tty_flush(int fd)
+void tty_sync(int fd)
{
ssize_t count;
- do
+ while (tty_buffer_count > 0)
{
count = write(fd, tty_buffer, tty_buffer_count);
if (count < 0)
{
// Error
- debug_printf("Write error while flushing tty buffer (%s)", strerror(errno));
+ tio_debug_printf("Write error while flushing tty buffer (%s)", strerror(errno));
break;
}
tty_buffer_count -= count;
+ fsync(fd);
+ tcdrain(fd);
}
- while (tty_buffer_count > 0);
// Reset
tty_buffer_write_ptr = tty_buffer;
ssize_t tty_write(int fd, const void *buffer, size_t count)
{
- ssize_t bytes_written = 0;
+ ssize_t retval = 0, bytes_written = 0;
+ size_t i;
+
+ if (map_o_ltu)
+ {
+ // Convert lower case to upper case
+ for (i = 0; i<count; i++)
+ {
+ *((unsigned char*)buffer+i) = toupper(*((unsigned char*)buffer+i));
+ }
+ }
- if (option.output_delay)
+ if (option.output_delay || option.output_line_delay)
{
// Write byte by byte with output delay
- for (size_t i=0; i<count; i++)
+ for (i=0; i<count; i++)
{
- ssize_t retval = write(fd, buffer, 1);
+ retval = write(fd, buffer, 1);
if (retval < 0)
{
// Error
- debug_printf("Write error (%s)", strerror(errno));
+ tio_debug_printf("Write error (%s)", strerror(errno));
break;
}
bytes_written += retval;
+
+ if (option.output_line_delay && *(unsigned char*)buffer == '\r')
+ {
+ delay(option.output_line_delay);
+ }
+
fsync(fd);
- delay(option.output_delay);
+ tcdrain(fd);
+
+ if (option.output_delay)
+ {
+ delay(option.output_delay);
+ }
}
}
else
{
- // Flush tty buffer if too full
+ // Force write of tty buffer if too full
if ((tty_buffer_count + count) > BUFSIZ)
{
- tty_flush(fd);
+ tty_sync(fd);
}
// Copy bytes to tty write buffer
ssize_t status = tty_write(fd, &hex_value, 1);
if (status < 0)
{
- warning_printf("Could not write to tty device");
+ tio_warning_printf("Could not write to tty device");
}
else
{
}
}
-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)
- {
- warning_printf("Could not get line state (%s)", strerror(errno));
- }
- else
+ if (line_mode == LINE_TOGGLE)
{
- 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));
+ }
+ } 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 (ioctl(fd, TIOCMSET, &state) < 0)
- warning_printf("Could not set line state (%s)", strerror(errno));
+ if (duration > 0)
+ {
+ tio_printf("Waiting %d ms", duration);
+ delay(duration);
+ }
+ toggle_line(line_name, mask, LINE_TOGGLE);
}
}
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 == KEY_CTRL_T)
+ if (previous_char == option.prefix_code)
{
/* Do not forward input char to output by default */
*forward = false;
{
case KEY_QUESTION:
tio_printf("Key commands:");
- 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 D Pulse 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 line states");
- 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 line timestamp mode");
- tio_printf(" ctrl-t v Show version");
+ 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 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 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);
+ tio_printf(" ctrl-%c v Show version", option.prefix_key);
break;
case KEY_SHIFT_L:
if (ioctl(fd, TIOCMGET, &state) < 0)
{
- warning_printf("Could not get line state (%s)", strerror(errno));
+ tio_warning_printf("Could not get line state (%s)", strerror(errno));
break;
}
tio_printf("Line states:");
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_SHIFT_D:
- toggle_line("DTR", TIOCM_DTR);
- delay(option.dtr_pulse_duration);
- 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_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:
break;
case KEY_T:
- /* Send ctrl-t key code upon ctrl-t t sequence */
- *output_char = KEY_CTRL_T;
- *forward = true;
- break;
-
- case KEY_SHIFT_T:
option.timestamp += 1;
switch (option.timestamp)
{
}
break;
+ case KEY_U:
+ map_o_ltu = !map_o_ltu;
+ break;
+
case KEY_V:
tio_printf("tio v%s", VERSION);
break;
/* Save current stdin settings */
if (tcgetattr(STDIN_FILENO, &stdin_old) < 0)
{
- error_printf("Saving current stdin settings failed");
+ tio_error_printf("Saving current stdin settings failed");
exit(EXIT_FAILURE);
}
status = tcsetattr(STDIN_FILENO, TCSANOW, &stdin_new);
if (status == -1)
{
- error_printf("Could not apply new stdin settings (%s)", strerror(errno));
+ tio_error_printf("Could not apply new stdin settings (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
/* Save current stdout settings */
if (tcgetattr(STDOUT_FILENO, &stdout_old) < 0)
{
- error_printf("Saving current stdio settings failed");
+ tio_error_printf("Saving current stdio settings failed");
exit(EXIT_FAILURE);
}
/* Reconfigure stdout (RAW configuration) */
cfmakeraw(&stdout_new);
+ /* Allow ^C / SIGINT (to allow termination when piping to tio) */
+ if (!interactive_mode)
+ {
+ stdout_new.c_lflag |= ISIG;
+ }
+
/* Control characters */
stdout_new.c_cc[VTIME] = 0; /* Inter-character timer unused */
stdout_new.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
status = tcsetattr(STDOUT_FILENO, TCSANOW, &stdout_new);
if (status == -1)
{
- error_printf("Could not apply new stdout settings (%s)", strerror(errno));
+ tio_error_printf("Could not apply new stdout settings (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
standard_baudrate = false;
break;
#else
- error_printf("Invalid baud rate");
+ tio_error_printf("Invalid baud rate");
exit(EXIT_FAILURE);
#endif
}
status = cfsetispeed(&tio, baudrate);
if (status == -1)
{
- error_printf("Could not configure input speed (%s)", strerror(errno));
+ tio_error_printf("Could not configure input speed (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
status = cfsetospeed(&tio, baudrate);
if (status == -1)
{
- error_printf("Could not configure output speed (%s)", strerror(errno));
+ tio_error_printf("Could not configure output speed (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
}
tio.c_cflag |= CS8;
break;
default:
- error_printf("Invalid data bits");
+ tio_error_printf("Invalid data bits");
exit(EXIT_FAILURE);
}
}
else
{
- error_printf("Invalid flow control");
+ tio_error_printf("Invalid flow control");
exit(EXIT_FAILURE);
}
tio.c_cflag |= CSTOPB;
break;
default:
- error_printf("Invalid stop bits");
+ tio_error_printf("Invalid stop bits");
exit(EXIT_FAILURE);
}
{
tio.c_cflag &= ~PARENB;
}
+ else if ( strcmp("mark", option.parity) == 0)
+ {
+ tio.c_cflag |= PARENB;
+ tio.c_cflag |= PARODD;
+ tio.c_cflag |= CMSPAR;
+ }
+ else if ( strcmp("space", option.parity) == 0)
+ {
+ tio.c_cflag |= PARENB;
+ tio.c_cflag &= ~PARODD;
+ tio.c_cflag |= CMSPAR;
+ }
else
{
- error_printf("Invalid parity");
+ tio_error_printf("Invalid parity");
exit(EXIT_FAILURE);
}
{
map_o_nl_crnl = true;
}
+ else if (strcmp(token, "OLTU") == 0)
+ {
+ map_o_ltu = true;
+ }
else
{
printf("Error: Unknown mapping flag %s\n", token);
/* Loop until device pops up */
while (true)
{
- if (first)
- {
- /* Don't wait first time */
- tv.tv_sec = 0;
- tv.tv_usec = 1;
- first = false;
- }
- else
+ if (interactive_mode)
{
- /* Wait up to 1 second */
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- }
+ /* In interactive mode, while waiting for tty device, we need to
+ * read from stdin to react on input key commands. */
+ if (first)
+ {
+ /* Don't wait first time */
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+ first = false;
+ }
+ else
+ {
+ /* Wait up to 1 second for input */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ }
- FD_ZERO(&rdfs);
- FD_SET(STDIN_FILENO, &rdfs);
- maxfd = MAX(STDIN_FILENO, socket_add_fds(&rdfs, false));
+ FD_ZERO(&rdfs);
+ FD_SET(STDIN_FILENO, &rdfs);
+ maxfd = MAX(STDIN_FILENO, socket_add_fds(&rdfs, false));
- /* Block until input becomes available or timeout */
- status = select(maxfd + 1, &rdfs, NULL, NULL, &tv);
- if (status > 0)
- {
- if (FD_ISSET(STDIN_FILENO, &rdfs))
+ /* Block until input becomes available or timeout */
+ status = select(maxfd + 1, &rdfs, NULL, NULL, &tv);
+ if (status > 0)
{
- /* Input from stdin ready */
-
- /* Read one character */
- status = read(STDIN_FILENO, &input_char, 1);
- if (status <= 0)
+ if (FD_ISSET(STDIN_FILENO, &rdfs))
{
- error_printf("Could not read from stdin");
- exit(EXIT_FAILURE);
- }
+ /* Input from stdin ready */
+
+ /* Read one character */
+ status = read(STDIN_FILENO, &input_char, 1);
+ if (status <= 0)
+ {
+ tio_error_printf("Could not read from stdin");
+ exit(EXIT_FAILURE);
+ }
- /* Handle commands */
- handle_command_sequence(input_char, previous_char, NULL, NULL);
+ /* Handle commands */
+ handle_command_sequence(input_char, previous_char, NULL, NULL);
- previous_char = input_char;
+ previous_char = input_char;
+ }
+ socket_handle_input(&rdfs, NULL);
+ }
+ else if (status == -1)
+ {
+ tio_error_printf("select() failed (%s)", strerror(errno));
+ exit(EXIT_FAILURE);
}
- socket_handle_input(&rdfs, NULL);
- }
- else if (status == -1)
- {
- error_printf("select() failed (%s)", strerror(errno));
- exit(EXIT_FAILURE);
}
/* Test for accessible device file */
}
else if (last_errno != errno)
{
- warning_printf("Could not open tty device (%s)", strerror(errno));
+ tio_warning_printf("Could not open tty device (%s)", strerror(errno));
tio_printf("Waiting for tty device..");
last_errno = errno;
}
+
+ if (!interactive_mode)
+ {
+ /* In non-interactive mode we do not need to handle input key
+ * commands so we simply sleep 1 second between checking for
+ * presence of tty device */
+ sleep(1);
+ }
}
}
status = tty_write(fd, crlf, 2);
if (status < 0)
{
- warning_printf("Could not write to tty device");
+ tio_warning_printf("Could not write to tty device");
}
tx_total += 2;
status = tty_write(fd, &output_char, 1);
if (status < 0)
{
- warning_printf("Could not write to tty device");
+ tio_warning_printf("Could not write to tty device");
}
/* Update transmit statistics */
fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0)
{
- error_printf_silent("Could not open tty device (%s)", strerror(errno));
+ tio_error_printf_silent("Could not open tty device (%s)", strerror(errno));
goto error_open;
}
/* Make sure device is of tty type */
if (!isatty(fd))
{
- error_printf("Not a tty device");
+ tio_error_printf("Not a tty device");
exit(EXIT_FAILURE);;
}
status = flock(fd, LOCK_EX | LOCK_NB);
if ((status == -1) && (errno == EWOULDBLOCK))
{
- error_printf("Device file is locked by another process");
+ tio_error_printf("Device file is locked by another process");
exit(EXIT_FAILURE);
}
/* Save current port settings */
if (tcgetattr(fd, &tio_old) < 0)
{
+ tio_error_printf_silent("Could not get port settings (%s)", strerror(errno));
goto error_tcgetattr;
}
#ifdef HAVE_IOSSIOSPEED
if (!standard_baudrate)
{
- /* OS X wants these fields left alone. We'll set baudrate with iossiospeed below. */
+ /* OS X wants these fields left alone before setting arbitrary baud rate */
tio.c_ispeed = tio_old.c_ispeed;
tio.c_ospeed = tio_old.c_ospeed;
}
status = tcsetattr(fd, TCSANOW, &tio);
if (status == -1)
{
- error_printf_silent("Could not apply port settings (%s)", strerror(errno));
+ tio_error_printf_silent("Could not apply port settings (%s)", strerror(errno));
goto error_tcsetattr;
}
-#ifdef HAVE_TERMIOS2
+ /* Set arbitrary baudrate (only works on supported platforms) */
if (!standard_baudrate)
{
- if (setspeed2(fd, option.baudrate) != 0)
+ if (setspeed(fd, option.baudrate) != 0)
{
- error_printf_silent("Could not set baudrate speed (%s)", strerror(errno));
+ tio_error_printf_silent("Could not set baudrate speed (%s)", strerror(errno));
goto error_setspeed;
}
}
-#endif
-
-#ifdef HAVE_IOSSIOSPEED
- if (!standard_baudrate)
- {
- if (iossiospeed(fd, option.baudrate) != 0)
- {
- error_printf_silent("Could not set baudrate speed (%s)", strerror(errno));
- goto error_setspeed;
- }
- }
-#endif
/* Input loop */
while (true)
if (bytes_read <= 0)
{
/* Error reading - device is likely unplugged */
- error_printf_silent("Could not read from tty device");
+ tio_error_printf_silent("Could not read from tty device");
goto error_read;
}
{
/* Input from stdin ready */
ssize_t bytes_read = read(STDIN_FILENO, input_buffer, BUFSIZ);
- if (bytes_read <= 0)
+ if (bytes_read < 0)
{
- error_printf_silent("Could not read from stdin");
+ tio_error_printf_silent("Could not read from stdin (%s)", strerror(errno));
goto error_read;
}
+ else if (bytes_read == 0)
+ {
+ /* Reached EOF (when piping to stdin) */
+ tty_sync(fd);
+ exit(EXIT_SUCCESS);
+ }
/* Process input byte by byte */
for (int i=0; i<bytes_read; i++)
if (interactive_mode)
{
- /* Do not forward ctrl-t key */
- if (input_char == KEY_CTRL_T)
+ /* Do not forward prefix key */
+ if (input_char == option.prefix_code)
{
forward = false;
}
/* Save previous key */
previous_char = input_char;
- if (print_mode == HEX)
+ if ((print_mode == HEX) && (forward))
{
if (!is_valid_hex(input_char))
{
- warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char);
+ tio_warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char);
forward = false;
}
}
}
}
- tty_flush(fd);
+ tty_sync(fd);
}
else
{
forward_to_tty(fd, output_char);
}
- tty_flush(fd);
+ tty_sync(fd);
}
}
else if (status == -1)
{
- error_printf("Error: select() failed (%s)", strerror(errno));
+ tio_error_printf("select() failed (%s)", strerror(errno));
exit(EXIT_FAILURE);
}
}
return TIO_SUCCESS;
-#if defined (HAVE_TERMIOS2) || defined (HAVE_IOSSIOSPEED)
error_setspeed:
-#endif
error_tcsetattr:
error_tcgetattr:
error_read:
#include <stdbool.h>
-#define KEY_QUESTION 0x3f
-#define KEY_B 0x62
-#define KEY_C 0x63
-#define KEY_E 0x65
-#define KEY_H 0x68
-#define KEY_L 0x6C
-#define KEY_Q 0x71
-#define KEY_S 0x73
-#define KEY_T 0x74
-#define KEY_SHIFT_T 0x54
-#define KEY_CTRL_T 0x14
-#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
-
extern bool interactive_mode;
void stdout_configure(void);
[wrap-git]
directory=libinih
url=https://github.com/benhoyt/inih.git
-revision=r55
+revision=r56