As a hardware and embedded engineer you come across of a lot of good and bad tools (hardware and software). One of our team’s nifty tools is a tty uart listener and protocol decoder. It is basically two FTDI UARTs that connect there RX to any signal on the UART of interest. Nothing fancy, but together with our Python script for decoding all the messages, this tool is very helpful for real time logging/debugging. It looks like this:
This tool works fine. Alternatively, we could use a logic analyser and implement a protocol decoder. A sales logic analyser, for example, would support this. But now we have a new challenge: A virtual serial connection to a maintenance tool on a computer. The maintenance tool is developed by a different department, but the serial protocol is the same. So for debugging purposes it would be great to use our Sniffer SW tool as it is. The approach is to create two virtual serial tty UART listeners on my Linux machine. This should look like this:
Ok, creating virtual tty (pty) and redirecting them sounds like what socat is made for that. Lets give it a try.
UDP try
UDP seems to be the first choice as the UART packets are connectionless like UDP. However, this approach did not work.
Port xx16 Portxx17 Virtual_A =><==( o===========o )===== Virtual_B
socat commands:
sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVA00,nonblock UDP-LISTEN:11316,reuseaddr,fork sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVB00,nonblock UDP-LISTEN:11317,reuseaddr,fork sudo socat -d -d UDP:localhost:11316 UDP:localhost:11317
The listener connects only after receiving data. As both listeners are not receiving data, the connection is never established → try TCP
sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVA00,nonblock TCP-LISTEN:11316,reuseaddr,fork sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVB00,nonblock TCP-LISTEN:11317,reuseaddr,fork sudo socat -d -d TCP:localhost:11316 TCP:localhost:11317
OK, the TCP approach works.
sudo on the virtual tty
It’s a bit tedious to always use sudo prevelage to connect to the virtual port.
This post helps: https://unix.stackexchange.com/questions/374916/how-to-use-virtual-serial-port-without-root-privileges
sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVA00,nonblock,group-late=dialout,mode=660 xxxx
Duplicate an UART stream
So, I want to duplicate a data stream. One stream as the normal data flow and the other to feed our debug tool :
Port xx16 Portxx17 Virtual_ttyVA00 ----( o-->----o )----- Virtual_ttyVB00 o \ Portxx18 ---->--o )---- Virtual_ttyVC00
And these are the socat commands for it:
sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVA00,nonblock,group-late=dialout,mode=660 TCP-LISTEN:11316,reuseaddr,fork sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVB00,nonblock,group-late=dialout,mode=660 TCP-LISTEN:11317,reuseaddr,fork sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVC00,nonblock,group-late=dialout,mode=660 TCP-LISTEN:11318,reuseaddr,fork sudo socat -u TCP:localhost:11316 - | tee >(socat - UDP:localhost:11317) >(socat - UDP:localhost:11318) > /dev/null
It works fine. I can connect to my three UART ports and send and receive both streams. Great, I think I’m done. Just need to put it all together.
The missing functionality in TCP
After hours of trying to set this up, I have finally isolated the missing piece of why it does not work. It’s not possible to split it into two paths.
Port xx16 Portxx17 Virtual_A ----( o------>---------o )----- Virtual_B o o \ / -----<-------
And these are the socat commands:
sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVA00,nonblock,group-late=dialout,mode=660 TCP-LISTEN:11316,reuseaddr,fork sudo socat -d -d PTY,raw,echo=0,link=/dev/ttyVB00,nonblock,group-late=dialout,mode=660 TCP-LISTEN:11317,reuseaddr,fork sudo socat -u TCP:localhost:11316 TCP:localhost:11317 sudo socat -U TCP:localhost:11316 TCP:localhost:11317
One path works, but when you try to send on the other path, it tries to send on the read connection and fails.
Using the socat -x option
Finally, I tried the -x option. So I got all the traffic streamed to stderr. I used tee to duplicate my stream (line 2) and separated the two streams with sed (lines 3 and 4). It took me a while to figure out why xxd (which converts hex output to binary output) was not outputting anything, so I had to add stdbuf -o0 to make sure there was no buffer.
sudo socat -x PTY,raw,echo=0,link=/dev/ttyVA00,nonblock,group-late=dialout,mode=660 PTY,raw,echo=0,link=/dev/ttyVB00,nonblock,group-late=dialout,mode=660 2> \ (tee \ (sed -u -n '/>/{n;p;}' > >(stdbuf -o0 xxd -r -p > >(sudo socat - PTY,raw,echo=0,link=/dev/ttyVC00,nonblock,group-late=dialout,mode=660))) \ (sed -u -n '/</{n;p;}' > >(stdbuf -o0 xxd -r -p > >(sudo socat - PTY,raw,echo=0,link=/dev/ttyVD00,nonblock,group-late=dialout,mode=660)))
Solved, finally.
socat help: http://rpm.pbone.net/manpage_idpl_25675711_numer_1_nazwa_socat.html