cminyard / serialsim

A Linux driver that simulates a serial port and allows control of modem lines and such
10 stars 1 forks source link

===================================== serialsim - A kernel serial simulator

:Author: Corey Minyard minyard@mvista.com / minyard@acm.org

The serialsim device is a serial simulator with echo and pipe devices. It is quite useful for testing programs that use serial ports.

This attempts to emulate a basic serial device. It uses the baud rate and sends the bytes through the loopback or pipe at approximately the speed it would on a normal serial device.

There is a python interface to the special ioctls for controlling the remote end of the termios. It is in the swig directory and is a standard autoconf build.

======== Building

"make" should build the module serialsim.ko, which can be loaded onto your system with insmod. You can do "sudo make install" which will install serialsim.ko in /lib/modules//local and serialsim.h in /usr/local/include/linux.

To build the python modules, cd to "swig" and run:

./configure make

======= Signing

If your kernel requires signed modules, you can sign. First copy the openssl.conf.example to openssl.conf. Edit it and change the distinguished_name, the value in [] referred to by distinguised_name, O, CN, and emailAddress fields to you.

This makefile uses a script from the kernel named "sign-file". You may have to modify KERNELBASE in the makefile to get it.

Then sign the module using:

make sign

This will create "private_key.der" and "public_key.der", and it will sign the module with "private_key.der". SAVE THESE KEYS! You will need them for signing in the future. The makefile will not overwrite them, but they could get deleted. You will also need to install the public key so the kernel can see it. After the keys are generated, do:

sudo mokutil --import public_key.der

It will ask for a password, input a new password. Then reboot the system. When the system reboots, it will prompt "Perform MOK management" Change to "Enroll MOK" and hit enter. Then in "Enroll MOK", select "Continue. Then enroll the key, and it will prompt you for the password you entered. Then you can load the module.

This info was pulled from:

https://www.janhendrikpeters.de/2021/05/11/obs-virtual-camera-secure-boot/

===== Using

The serialsim.ko module creates two types of devices. Echo devices simply echo back the data to the same device. These devices will appear as /dev/ttyEcho.

Pipe devices will transfer the data between two devices. The devices will appear as /dev/ttyPipeA and /dev/ttyPipeB. And data written to PipeA reads from PipeB, and vice-versa.

You may create an arbitrary number of devices by setting the nr_echo_ports and nr_pipe_ports module parameters. The default is four for both.

You may also dynamically create devices for your use. If you open /dev/ttyEcho, you can use the SERIALSIM_ALLOC_ID ioctl to create a new echo device. It will return the echo device number N (/dev/ttyEchoN) that you can then open. Note that it may take a little time for udev to create the device file. If you use the SERIALSIM_FREE_ID ioctl and pass in the echo device number N, it will free the device. Also, if you close the /dev/ttyEcho file, it will free all devices you have allocated with that file.

There is also a /dev/ttyPipe device that is similar to /dev/ttyEcho, but creates pipe devices.

By default you can create up to 16 dynamic devices of each type. You may change these numbers with the nr_dyn_echo_ports and nr_dyn_pipe_ports module parameters.

Dynamic devices are useful if you want to allocate a serial port or serial port pair for testing but don't really care what number it is. That makes it easier to do parallel testing.

This driver supports modifying the modem control lines and injecting various serial errors. It also supports a simulated null modem between the two pipes, or in a loopback on the echo device.

By default a pipe or echo comes up in null modem configuration, meaning that the DTR line is hooked to the DSR and CD lines on the other side and the RTS line on one side is hooked to the CTS line on the other side.

The RTS and CTS lines don't currently do anything for flow control.

You can modify null modem and control the lines individually through an interface in /sys/class/tty/ttyECHO/ctrl, /sys/class/tty/ttyPipeA/ctrl, and /sys/class/tty/ttyPipeB/ctrl. The following may be written to those files:

[+-]nullmodem enable/disable null modem

[+-]cd enable/disable Carrier Detect (no effect if +nullmodem)

[+-]dsr enable/disable Data Set Ready (no effect if +nullmodem)

[+-]cts enable/disable Clear To Send (no effect if +nullmodem)

[+-]ring enable/disable Ring

frame inject a frame error on the next byte

parity inject a parity error on the next byte

overrun inject an overrun error on the next byte

The contents of the above files has the following format:

tty[Echo|PipeA|PipeB]

where is the modem control values above (not frame, parity, or overrun) with the following added: [+-]dtr value of the Data Terminal Ready [+-]rts value of the Request To Send The above values are not settable through this interface, they are set through the serial port interface itself. So, for instance, ttyEcho0 comes up in the following state:: # cat /sys/class/tty/ttyEcho0/ctrl ttyEcho0: +nullmodem -cd -dsr -cts -ring -dtr -rts If something connects, it will become:: ttyEcho0: +nullmodem +cd +dsr +cts -ring +dtr +rts To enable ring:: # echo "+ring" >/sys/class/tty/ttyEcho0/ctrl # cat /sys/class/tty/ttyEcho0/ctrl ttyEcho0: +nullmodem +cd +dsr +cts +ring +dtr +rts Now disable NULL modem and the CD line:: # echo "-nullmodem -cd" >/sys/class/tty/ttyEcho0/ctrl # cat /sys/class/tty/ttyEcho0/ctrl ttyEcho0: -nullmodem -cd -dsr -cts +ring -dtr -rts Note that these settings are for the side you are modifying. So if you set nullmodem on ttyPipeA0, that controls whether the DTR/RTS lines from ttyPipeB0 affect ttyPipeA0. It doesn't affect ttyPipeB's modem control lines. The PIPEA and PIPEB devices also have the ability to set these values for the other end via an ioctl. The following ioctls are available: TIOCSERSNULLMODEM Set the null modem value, the arg is a boolean. TIOCSERSREMMCTRL Set the modem control lines, bits 16-31 of the arg is a 16-bit mask telling which values to set, bits 0-15 are the actual values. Settable values are TIOCM_CAR, TIOCM_CTS, TIOCM_DSR, and TIOC_RNG. If NULLMODEM is set to true, then only TIOC_RNG is settable. The DTR and RTS lines are not here, you can set them through the normal interface. TIOCSERSREMERR Send an error or errors on the next sent byte. arg is a bitwise OR of (1 << TTY_xxx). Allowed errors are TTY_BREAK, TTY_FRAME, TTY_PARITY, and TTY_OVERRUN. TIOCSERGREMTERMIOS Return the termios structure for the other side of the pipe. arg is a pointer to a standard termios struct. TIOCSERGREMRS485 Return the remote RS485 settings, arg is a pointer to a struct serial_rs485. Note that unlike the sysfs interface, these ioctls affect the other end. So setting nullmodem on the ttyPipeB0 interface sets whether the DTR/RTS lines on ttyPipeB0 affect ttyPipeA0. ================ Python Interface ================ The python interface is a straight conversion of the C interface into python. It is in the serialsim python module and has the following interfaces:: termios = get_remote_termios(fd) The termios are the standard python termios:: rs485 = get_remote_rs485(fd) rs485 is a string representation of the rs485 paramters, in the form:: " [