Git Product home page Git Product logo

serialsim's Introduction

serialsim - A kernel serial simulator

Author

Corey Minyard <[email protected]> / <[email protected]>

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/<kernver>/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<n>.

Pipe devices will transfer the data between two devices. The devices will appear as /dev/ttyPipeA<n> and /dev/ttyPipeB<n>. 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<n>/ctrl, /sys/class/tty/ttyPipeA<n>/ctrl, and /sys/class/tty/ttyPipeB<n>/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[EchoPipeB]<n>

<mctrl values>

where <mctrl values> 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:

"<delay_rts_before_send> <delay_rts_after_send> [<option> []]"

The two given values are integers, options are:

enabled
rts_on_send
rts_after_send
rx_during_tx
terminate_bus

You will need to review RS485 documentation for details. To get and set the modem control lines:

set_remote_modem_ctl(fd, val)
val = get_remote_modem_ctl(fd);

The value is a bitmask of:

SERIALSIM_TIOCM_CAR
SERIALSIM_TIOCM_CTS
SERIALSIM_TIOCM_DSR
SERIALSIM_TIOCM_RNG
SERIALSIM_TIOCM_DTR
SERIALSIM_TIOCM_RTS

You cannot set DTR or RTS, they are outputs from the other side:

set_remote_serial_err(fd, val)
val = get_remote_serial_err(fd);

You can inject serial errors on the other end. The value is a bitmask of:

SERIALSIM_TTY_BREAK
SERIALSIM_TTY_FRAME
SERIALSIM_TTY_PARITY
SERIALSIM_TTY_OVERRUN

Hopefully the meanings of these are obvious. The null modem setting for the remote serial port:

set_remote_null_modem(fd, bool_val)
bool_val = get_remote_null_modem(fd);

There is also the:

alloc_id()
free_id()

functions to allocate and free dynamic devices. For instance, to create a dynamic pipe, do something like:

import serialsim
p = open("/dev/ttyPipe", "w")
id = serialsim.alloc_id(p.fileno())
pipea = "/dev/ttyPipeA%d" % id
pipeb = "/dev/ttyPipeB%d" % id

# Use pipea and pipeb here

# Not strictly necessary, the close will do this, too.
serialsim.free_id(p.fileno())

p.close()

Some higher level functions exist to help out:

(num, pipea, pipeb) = serialsim.alloc_pipe()
# pipea will be "/dev/ttyPipeA%d" % num, pipeb similar
serialsim.free_pipe(num)

And the equivalent for echo:

(num, echo) = serialsim.alloc_echo()
# echo will be "/dev/ttyEcho%d" % num
serialsim.free_echo(num)

These keep in internal opened /dev/ttyPipe and /dev/ttyEcho. Also, more importantly, they wait for the device files to appear. If the device(s) don't appear in a timely manner, these will raise an exception.

serialsim's People

Contributors

cminyard avatar jameshilliard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

jameshilliard

serialsim's Issues

Python asyncio protocol support

I was thinking it might make sense to wire this up somehow to a python asyncio protocol.

I wrote something that acts as a virtual serial port proxy using a pty and asyncio tcp protocol with ser2net but that of course is missing some features this has.

Something like this is handy if one wants to run a serial based application on a development machine when the serial device is attached to a separate embedded system(which may not have the right environment to run said application itself) in addition to allowing for debugging/manipulating of commands bidirectionally between the application and device(think of it like a serial based mitmproxy).

serialproxy.py

I'm wondering, was there a reason for using swig for the python interface of serialsim? Wouldn't it be easier to just use normal python ioctl calls?

Unable to build python module

After cloning, I ran the reconf script from ser2net, configure, and make. It failed to build and appears that there is a file missing. Full log follows.

✔ ~/workspace/serialsim/swig [master|✔]
15:37 $ ../../ser2net/reconf
libtoolize: putting auxiliary files in '.'.
libtoolize: linking file './ltmain.sh'
configure.ac:4: installing './ar-lib'
configure.ac:3: installing './compile'
configure.ac:5: installing './config.guess'
configure.ac:5: installing './config.sub'
configure.ac:2: installing './install-sh'
configure.ac:2: installing './missing'
Makefile.am: installing './INSTALL'
Makefile.am: installing './depcomp'
✔ ~/workspace/serialsim/swig [master|…13]
15:37 $ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of gcc... gcc3
checking for ar... ar
checking the archiver (ar) interface... ar
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking how to print strings... printf
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking how to convert x86_64-pc-linux-gnu file names to x86_64-pc-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for archiver @file support... @
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from gcc object... ok
checking for sysroot... no
checking for a working dd... /bin/dd
checking how to truncate binary pipes... /bin/dd bs=4096 count=1
checking for mt... mt
checking if mt is a manifest tool... no
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fPIC -DPIC
checking if gcc PIC flag -fPIC -DPIC works... yes
checking if gcc static flag -static works... yes
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
checking whether make supports nested variables... (cached) yes
checking for ANSI C header files... (cached) yes
checking for swig... no
checking for swig3.0... no
checking for swig2.0... no
checking for python3... /usr/bin/python3
checking for python3... (cached) /usr/bin/python3
checking for a version of Python >= '2.1.0'... yes
checking for the distutils Python package... yes
checking for Python include path... -I/usr/include/python3.7m
checking for Python library path... -L/usr/lib -lpython3.7m
checking for Python site-packages path... /usr/lib/python3/dist-packages
checking python extra libraries... -lcrypt -lpthread -ldl -lutil -lm
checking python extra linking flags... -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
checking consistency of all components of python development environment... yes
checking for python version... 3.7.5
checking for python threads... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
config.status: executing libtool commands
✔ ~/workspace/serialsim/swig [master|…18]
15:37 $ make
CC serialsim.lo
DPACKAGE_NAME="serialsim" -DPACKAGE_TARNAME="serialsim" -DPACKAGE_VERSION="1.0-rc2" -DPACKAGE_STRING="serialsim\ 1.0-rc2" -DPACKAGE_BUGREPORT="[email protected]" -DPACKAGE_URL="" -DPACKAGE="serialsim" -DVERSION="1.0-rc2" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=".libs/" -DSTDC_HEADERS=1 -DHAVE_PYTHON="3.7" -python -py3 -o serialsim_wrap.c
serialsim.i
/bin/bash: -DPACKAGE_TARNAME="serialsim": command not found
make: [Makefile:860: serialsim_wrap.c] Error 127 (ignored)
CC serialsim_wrap.lo
gcc: error: serialsim_wrap.c: No such file or directory
gcc: fatal error: no input files
compilation terminated.
make: *** [Makefile:482: serialsim_wrap.lo] Error 1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.