面向 Linux 的虚拟串行端口

我需要在 Linux 上测试一个串口应用程序,但是,我的测试机器只有一个串口。

有没有一种方法可以向 Linux 添加虚拟串行端口,并通过 shell 或脚本模拟设备来测试我的应用程序?

注意: 我不能重新映射端口,它硬编码在 ttys2上,我需要在编写应用程序时测试它。

234033 次浏览

你能使用 USB-> RS232适配器吗?我有一些,他们只是使用 FTDI 驱动程序。然后,您应该能够将/dev/ttyUSB0(或创建的任何东西)重命名为/dev/ttyS2。

我能想到三种选择:

执行 RFC 2217

RFC 2217 涵盖了 TCP/IP 标准的 com 端口,允许一个系统上的客户端模拟本地程序的串行端口,同时透明地向另一个实际上有串行端口的系统上的服务器发送和接收数据和控制信号。这是 高级别概览高级别概览

你要做的就是找到或者实现一个客户端端口驱动程序,在你的 PC 上实现系统的客户端——看起来像是一个真正的串行端口,但实际上是将所有东西穿梭到一个服务器上。你也许能够从 Digi,Lantronix 等免费获得这个驱动程序,以支持他们真正的独立串行端口服务器。

然后,您将在另一个程序中本地实现连接的服务器端-允许客户端根据需要进行连接并发出数据和控制命令。

这可能不是小事,但是 RFC 是存在的,而且您可能能够找到一个实现连接的一个或两个方面的开放源码项目。

修改 Linux 串口驱动程序

另外,Linux 的串行端口驱动程序源代码也很容易获得。接下来,对硬件控制部分进行整理,让这个驱动程序运行两个/dev/ttySx 端口,作为一个简单的回路。然后将您的实际程序连接到 ttyS2,并将您的模拟器连接到另一个 ttySx。

在一个环回中使用两个 USB < —— > 串行电缆

但是现在最简单的事情是什么呢?花40美元买两个串行端口 USB 设备,把它们连接在一起(空调制解调器) ,实际上有两个真正的串行端口——一个用于你正在测试的程序,一个用于你的模拟器。

亚当

您可以使用 pty (“伪电传打字机”,其中串行端口是“真正的电传打字机”)。从一端打开 /dev/ptyp5,然后将您的程序附加到 /dev/ttyp5; ttyp5将像串行端口一样工作,但是将通过/dev/ptype5发送/接收它所做的所有事情。

如果您确实需要它与一个名为 /dev/ttys2的文件进行通信,那么只需将旧的 /dev/ttys2移到一边,创建一个从 ptyp5ttys2的符号链接。

当然,除了 ptyp5以外,您还可以使用其他一些数字。也许选择一个数字大的,以避免重复,因为您的所有登录终端也将使用 ptys。

维基百科有更多关于 ptys 的内容: http://en.wikipedia.org/wiki/Pseudo_terminal

您可能想看看 Tibbo VSPDL,它使用内核驱动程序创建了一个 linux 虚拟串行端口——它看起来很新,现在可以下载(beta 版)。目前还不确定是否有这个许可证,或者他们是否希望在未来将其商业化。

还有其他商业选择,如 http://www.ttyredirector.com/

在开放源码中,连环杀手(GPL)也可以使用 UnixPTY 做你想做的事情。它以“原始形式”将串行数据传输到网络套接字; 在创建端口时必须完成类似于 STTY 的终端参数设置,后来像 RFC2217中描述的那样更改它们似乎不受支持。除了需要提前设置端口速度等之外,您应该能够运行两个 reary 实例来创建一个类似 com0com 的虚拟 nulldem。

Socat (同样是 gPL)就像是一个扩展版的 RemSeries,有更多的选项,包括一个“ PTY”方法,用于将 PTY 重定向到其他地方,这可以是 Socat 的另一个实例。对于 Unit tets 来说,socat 可能优于 re 串行,因为您可以直接将 cat 文件放入 PTY 中。请参见页面上的 PTY 的例子。“贡献”下的 补丁是存在的为协商串行线设置提供 RFC2217支持。

使用前面答案中提供的链接,我使用 Virtual Serial Port 在 C + + 中编写了一个小示例。我把代码输入 GitHub: https://github.com/cymait/virtual-serial-port-example

这些代码非常容易理解。首先,通过运行。/main master,它将打印到设备正在使用的 stderr。在那之后,你就可以。/主从设备,其中设备是第一个命令中打印的设备。

就是这样,这两个过程之间有一个双向链接。

使用此示例,您可以通过发送所有类型的数据来测试应用程序,并查看它是否正常工作。

此外,您总是可以对设备进行符号链接,因此不需要重新编译正在测试的应用程序。

使用 socat:

例如:

socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11

还有 tty0tty http://sourceforge.net/projects/tty0tty/,它是一个真正的 linux 空调制解调器模拟器。

它是一个简单的内核模块——一个小的源文件。我不知道为什么只有 Source Forge 反对它,但它对我很有用。它最好的地方就是还能模拟硬件引脚(RTC/CTS DSR/DTR)。它甚至实现了 TIOCMGET/TIOCMSET 和 TIOCMIWAIT iotcl 命令!

在最近的内核上,可能会出现编译错误。这很容易解决。只需在模块/tty0tty.c source 的顶部插入几行代码(在 include 之后) :

#ifndef init_MUTEX
#define init_MUTEX(x) sema_init((x),1)
#endif

当加载模块时,它创建4对串行端口。设备是/dev/tnt0到/dev/tnt7,其中 tnt0连接到 tnt1,tnt2连接到 tnt3,等等。 您可能需要修复文件权限才能使用设备。

编辑:

我想我的热情有点过头了。虽然司机看起来很有希望,但看起来很不稳定。我不确定,但我觉得它撞坏了我在家工作的办公室里的一台机器。我周一回办公室才能确认。

第二件事是 TIOCMIWAIT 不起作用。这段代码似乎是从一些“ Tiny tty”示例代码中复制的。TIOCMIWAIT 的处理似乎已经就绪,但是它永远不会被唤醒,因为缺少对唤醒 _ up _ intertionable ()的相应调用。

编辑:

办公室的事故确实是司机的错。缺少初始化,完全未测试的 TIOCMIWAIT 代码导致计算机崩溃。

我花了昨天和今天重写驱动程序。之前有很多问题,但现在对我来说很有用。由驱动程序管理的硬件流控制仍然缺少代码,但我不需要它,因为我将使用来自用户模式代码的 TIOCMGET/TIOCMSET/TIOCMIWAIT 自己管理引脚。

如果有人对我的代码版本感兴趣,请给我发信息,我会发给你。

补充@slonik 的回答。

你可以通过下面的步骤测试 socat 来创建 Virtual Serial Port (在 Ubuntu 12.04上测试) :

打开一个终端(我们称之为终端0)并执行它:

socat -d -d pty,raw,echo=0 pty,raw,echo=0

以上代码返回:

2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/2
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/3
2013/11/01 13:47:27 socat[2506] N starting data transfer loop with FDs [3,3] and [5,5]

打开另一个终端并写入(终端1) :

cat < /dev/pts/2

这个命令的端口名可以根据个人电脑改变。这取决于以前的输出。

2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/**2**
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/**3**
2013/11/01 13:47:27 socat[2506] N starting data transfer loop with FDs

您应该使用突出显示区域的可用号码。

打开另一个终端并写入(终端2) :

echo "Test" > /dev/pts/3

现在回到1号终端,您将看到字符串“ Test”。

$ socat -d -d pty,link=/tmp/vserial1,raw,echo=0 pty,link=/tmp/vserial2,raw,echo=0

将为在 /dev/pts/*中生成的虚拟串行端口生成 /tmp/vserial1/tmp/vserial2的符号链接

资源

结合所有其他有用的答案,我发现下面的命令对于测试不同类型的 Linux 发行版非常有用,因为不能保证你是 每次都会得到相同的/dev/pt/# 和/或者你需要同时测试多个 psuedo 串行设备和连接。

parallel 'i="{1}"; socat -d -d pty,raw,echo=0,link=$HOME/pty{1} pty,raw,echo=0,link=$HOME/pty$(($i+1))' ::: $(seq 0 2 3;)

分析一下:

parallel对提供给它的每个参数运行相同的命令。 例如,如果我们用 --dryrun标志来运行它,它会给我们:

i="0"; socat -d -d pty,raw,echo=0,link=$HOME/pty0 pty,raw,echo=0,link=$HOME/pty$(($i+1))
i="2"; socat -d -d pty,raw,echo=0,link=$HOME/pty2 pty,raw,echo=0,link=$HOME/pty$(($i+1))

这是由于 $(seq x y z;)在最后,其中 X = 开始 # ,y = 递增,z = 结束 # (或者需要产生的设备的 #)

parallel 'i="{1}"; echo "make psuedo_devices {1} $(($i+1))"' ::: $(seq 0 2 3;)

产出:

make psuedo_devices 0 1
make psuedo_devices 2 3

将所有这些收集在一起,最后使用上面的命令符号将适当的 psuedo 设备链接在一起,而不管/dev/pt/中的内容是什么,都通过 link标志链接到提供给 socat 的任何目录。

pstree -c -a $PROC_ID给出:

perl /usr/bin/parallel i="{1}"; socat -d -d pty,raw,echo=0,link=$HOME/pty{1} pty,raw,echo=0,link=$HOME/pty$(($i+1)) ::: 0 2
├─bash -c i="0"; socat -d -d pty,raw,echo=0,link=$HOME/pty0 pty,raw,echo=0,link=$HOME/pty$(($i+1))
│   └─socat -d -d pty,raw,echo=0,link=/home/user/pty0 pty,raw,echo=0,link=/home/user/pty1
└─bash -c i="2"; socat -d -d pty,raw,echo=0,link=$HOME/pty2 pty,raw,echo=0,link=$HOME/pty$(($i+1))
└─socat -d -d pty,raw,echo=0,link=/home/user/pty2 pty,raw,echo=0,link=/home/user/pty3

Ls-l $HOME/pty * 收益率:

lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty0 -> /dev/pts/4
lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty1 -> /dev/pts/6
lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty2 -> /dev/pts/7
lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty3 -> /dev/pts/8

这都是因为我试图在一个平台上运行测试,在这个平台上我需要生成大量的 mach 串行连接,并通过集装箱化(Docker)测试它们的输入/输出。希望有人觉得有用。