Setting Up Virtual Serial Ports and Modems in Linux

Introduction

A long time ago, in decades far, far away, before the Internet and TCP/IP, in the universe of digital telecommunications, serial communications reigned supreme. These were the days of RS-232 comports, modems and dial-up BBSes.

But this is current year. RS232 ports, screeching modems and dial-up BBSes are relics of a bygone era. And even should your computer still have a physical comport and you could manage to pick up an old modem at a flea sale, there's nothing to connect to; dial-up BBSes moved online decades ago. So what's a retro guy to do? Virtualize.

In this tutorial, for Linux users, we will install and configure three components:

tty0tty - null modem emulator; creates pairs of virtual comports connected by virtual null serial cables
tcpser - emulates a Hayes-compatible modem; creates piped tunnels between serial and tcp ports
minicom - communications package for communicating over a serial port

Before beginning, make sure your system is up-to-date and you have your kernel headers and source installed and the Linux development environment in place. For Debian-based distros, do the following:

$> sudo apt-get update
$> sudo apt-get install linux-headers-$(uname -r)
$> apt-get source linux-image-$(uname -r)
$> sudo apt-get install dpkg-dev

tty0tty—Virtual ComPorts

tty0tty is a kernel module that creates virtual serial ports that will look and behave exactly like physical RS232 hardware ports on your system. To install and configure tty0tty, run the following commands (you may need to change /usr/local/bin/ to something appropriate for your system):

$> wget https://github.com/freemed/tty0tty/archive/master.zip
$> unzip master.zip
$> cd tty0tty-master/
$> make
$> sudo cp pts/tty0tty /usr/local/bin/
$> sudo cp module/tty0tty.ko /lib/modules/$(uname -r)/kernel/drivers/misc/
$> sudo depmod
$> sudo modprobe tty0tty
$> sudo chmod 666 /dev/tnt*
$> cd ..
$> rm -r tty0tty-master/
$> rm master.zip

To verify tty0tty is running, do:

$> ls -l /dev/tnt*
crw-rw-rw- 1 root root 246, 0 May 24 08:32 /dev/tnt0
crw-rw-rw- 1 root root 246, 1 May 24 08:32 /dev/tnt1
crw-rw-rw- 1 root root 246, 2 May 24 08:32 /dev/tnt2
crw-rw-rw- 1 root root 246, 3 May 24 08:32 /dev/tnt3
crw-rw-rw- 1 root root 246, 4 May 24 08:32 /dev/tnt4
crw-rw-rw- 1 root root 246, 5 May 24 08:32 /dev/tnt5
crw-rw-rw- 1 root root 246, 6 May 24 08:32 /dev/tnt6
crw-rw-rw- 1 root root 246, 7 May 24 08:32 /dev/tnt7

Make sure the permissions on the ports are set to crw-rw-rw-.

You'll undoubtedly want tty0tty to load whenever you start your system, so edit either /etc/modules or /etc/modules.conf (depending on which your system has) and add the line tty0tty.

Note: Should you ever update your Linux kernel, you will need to reinstall tty0tty.

We're now ready to install tcpser.

Virtual Modem—tcpser

tcpser is a virtual Hayes-compatible modem emulator which sits between a serial port and the internet, translating between RS232 and tcp/ip, so that anything talking to the comport will think it's seeing a Hayes modem. There are several ways to obtain tcpser.

Repositories

If tcpser is available in your Linux distro's repositories, this may be the easiest way to install it. Under Debian-based distros, do:

$> sudo apt-get install tcpser

Debian package

After Ubuntu 18.04 (Bionic) tcpser was removed from the Ubuntu repositories. However, the Debian .deb package can still be obtained and installed manually from the Bionic repositories. For the AMD64 version (visit here or here for other versions), do this:

$> wget http://security.ubuntu.com/ubuntu/pool/universe/t/tcpser/tcpser_1.0rc12-2build1_amd64.deb
$> sudo dpkg -i tcpser_1.0rc12-2build1_amd64.deb
$> rm tcpser_1.0rc12-2build1_amd64.deb

Manual installation

If the above methods fail, there are two forks available at Github. To install from fozztexx (or see go4retro):

$> wget https://github.com/FozzTexx/tcpser/archive/master.zip
$> unzip master.zip
$> cd tcpser-master
$> make
$> sudo cp -p tcpser /usr/local/bin
$> cd ..
$> rm -r tcpser-master/
$> rm master.zip

Run tcpser

Whichever method above you used to install tcpser, load it now:

$> sudo tcpser -d /dev/tnt1 -s 115200 -p 6400 -n5551212=vert.synchro.net:23 &

The above will load tcpser, establish a pipe between /dev/tnt1 and ip port 6400, set an emulated speed of 115200 baud, and associate the phone number 555-1212 with vert.synchro.net port 23. Anything communicating with /dev/tnt0 will now think it's talking to a Hayes modem. Run man tcpser or see here for much more info.

To verify that tcpser is running, run the following. If you get no response, tcpser is not running.

$> ps cax | grep tcpser
27106 pts/10 Sl 0:00 tcpser
27107 pts/10 Sl 0:00 tcpser
27108 pts/10 Sl 0:00 tcpser
27109 pts/10 Sl 0:00 tcpser

Comm Software—Minicom

minicom is probably available from your repository:

$> sudo apt-get install minicom

Alternatively, it can be installed manually:

$> wget https://github.com/Distrotech/minicom/archive/master.zip
$> unzip master.zip
$> cd minicom-master/
$> ./configure
$> make
$> make install
$> cd ..
$> rm -r minicom-master/
$> rm master.zip

Now we need to configure minicom.

$> sudo minicom -s

Select Serial port setup, type A and change the default serial port to /dev/tnt0, then <Esc> out. Make any other changes you'd like, then select Save setup as dfl, then Exit from minicom.

You will need to give every user who wishes to use minicom permissions. On Debian systems, this means adding them to the dialout group.

Finally, restart minicom:

$> minicom

You should see something like this (note Port /dev/tnt0):

Welcome to minicom 2.7.1

OPTIONS: I18n
Compiled on Dec 23 2019, 02:06:26.
Port /dev/tnt0

Press CTRL-A Z for help on special keys

















CTRL-A Z for help | 115200 8N1 | NOR | Minicom 2.7 | ANSI | Offline | tnt0

If you see the message minicom: cannot open /dev/tnt0: Permission denied it probably means you forgot to change permissions on the virtual ports, so run sudo chmod 666 /dev/tnt* then start minicom.

So does it work? Type ATDT5551212 and press enter. Minicom should connect to the telnet BBS you configured tcpser for when you ran it (vert.synchro.net:23 in our example).

Minicom has a built-in phone directory, which you can access via Ctrl-A D. Here you can store frequently called numbers, but you'll need to associate each number to a BBS by adding -n parameters to the tcpser command line.

To exit minicom, type Ctrl-A X.

Epilogue

We're done. From now on, as long as tty0tty and tcpser are configured and running as above, anything talking to /dev/tnt0 will think it's seeing a Hayes-compatible modem. For its part, all serial communications sent to a tcpser virtual modem will be routed out its configured tcp/ip port, while traffic coming into that port will be routed to the virtual RS232 com port.

You can run additional instances of tcpser, attaching them to the odd-numbered /dev/tnt*, and each corresponding even-numbered /dev/tnt* will appear to have a modem connected. Just remember to change the assigned tcp/ip port to something unique for each virtual modem. And finally, while tty0tty defaults to four port-pairs, with a simple edit to the source, it can support up to 128 port pairs.

Addenda

DOSBox

If you run the DOSBox DOS emulator, you can configure it to use the virtual modem. Edit your DOSBOX.CONF configuration file, and in the serial section set:

serial1=directserial realport:tnt0
serial2=directserial realport:tnt2

This will give your DOS session two comports, allowing you to run two comm apps concurrently, say Renegade BBS on one while using the other for dialing out. Just configure Renegade to use COM2 in DOSBox and your comm software (e.g., QModem) for COM1.

BASH Script

If you use the BASH command shell, once everything above is running, you can use the following BASH script to control things:

#!/bin/bash

# VERSION: 2020.12.12

setports() { # Ports already exist? Permissions set properly?
if [ ! -f /dev/tnt0 ]; then sudo modprobe tty0tty; fi
if [ $(stat -c %a /dev/tnt0) != 777 ]; then sudo chmod +777 /dev/tnt*; fi
}

setmodems() {
ps cax | grep tcpser > nul && return # if tcpser already running, just leave
sudo tcpser -d /dev/tnt1 -s 115200 -p 6400 -n6352165=jenandcal.familyds.org:2323 &
sudo tcpser -d /dev/tnt3 -s 115200 -p 6401 -n6352165=jenandcal.familyds.org:2323 &
sudo tcpser -d /dev/tnt5 -s 115200 -p 6402 -n6352165=jenandcal.familyds.org:2323 &
sudo tcpser -d /dev/tnt7 -s 115200 -p 6403 -n6352165=jenandcal.familyds.org:2323 &
}

downmodems() {
sudo killall tcpser
}

downports() {
sudo modprobe -r tty0tty
}

showhelp() {
echo "Modem usage:"
echo "vmodem p|ports ; Load tty0tty to create virtual serial ports"
echo "vmodem m|modems ; Load four virtual modems using tcpser"
echo "vmodem d|down ; Unload tcpser and tty0tty"
echo "vmodem a|all ; Load tty0tty and tcpser"
echo "vmodem r|restart ; Unload/reload tty0tty and tcpser"
}

case $1 in

p|ports)
setports
;;
m|modems)
setmodems
;;
d|down)
downmodems
downports
;;
a|all)
setports
setmodems
;;
r|restart)
downmodems
downports
setports
setmodems
;;
*)
showhelp
;;
esac

exit