Linux tty端口打开后自发发送数据

Rob*_*ert 5 linux serial-port tty termios

当打开插入基于 ARM9 的嵌入式板的 USB 主机的基于 FDTI USB UART 的串行端口时,它会自发传输数据。它在打开时就正确执行此操作,甚至在设置比特率或对 fd 执行任何其他操作之前。

发送的数据总是或多或少相同:

^@^@^@^@^@
Run Code Online (Sandbox Code Playgroud)

用十六进制表示为:0x5e 0x40 0x5e 0x40 0x5e 0x40 0x5e 0x40 0x5e 0x40。重复次数从一对到最多 256 对不等。

我追溯了这个数据,它源自 tty 内核工作队列。因此,这不是由有错误的驱动程序和未正确初始化的传输队列或沿该线的任何内容引起的。显然,内核在打开系统调用时有意传输此数据。

仅在重新启动后或插入 FTDI USB UART 后首次打开 tty 端口时才会出现此问题。(重新启动会重新启动 USB 设备,因此这两者基本相同。)

是的,我确实意识到 ^@ 意味着“\0”。而且 NULL 还可以用来实现延迟。从手册中:

延迟位指定当某些字符发送到终端时,传输停止多长时间以允许机械或其他运动。在所有情况下,值 0 均表示没有延迟。如果设置了 OFILL,则应延迟传输填充字符,而不是定时延迟。这对于只需要最小延迟的高波特率终端很有用。如果设置了OFDEL,则填充字符为DEL;否则,NUL。

但是,我的 TTY 设置并不表明此类设置处于活动状态:

root@IVW78103 ~$ stty -F /dev/ttyUSB0 -a
speed 9600 baud;stty: /dev/ttyUSB0
 line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke
Run Code Online (Sandbox Code Playgroud)

现在我得到的最后一个也是最重要的线索是 FTDI 串行端口端的设备在加电时输出大量数据(= USB 插件)。当我隔离 FTDI RxD 线时,问题就不会发生。显然,内核、termios 或 tty 对此数据感到不满。

现在的问题是:如何防止发送^@数据?因为FTDI串口端的设备对其响应不佳。

编辑:输出似乎是 termios 回显输入。如果我通过在 .../drivers/tty/n_tty.c:process_echoes:639 中注释掉这段代码来对内核进行脑叶切除,症状就会消失

tty_put_char(tty, '^');
tty_put_char(tty, op ^ 0100);
Run Code Online (Sandbox Code Playgroud)

我仍然困惑为什么它会回显在打开 tty 端口之前很久收到的数据。我可能是 USB UART 无法真正刷新所有数据的副作用。

Rob*_*ert 1

此问题是由默认启用的 termios echo 和不支持刷新的 FTDI tty 设备驱动程序组合引起的。

Greg 在他的评论中解释了后者。他的评论主要是关于 TCOFLUSH,但显然 TCIFLUSH 也不被支持。该评论专门针对 FDTI tty 驱动程序,但我确信这同样适用于许多(如果不是全部)其他 USB UART 驱动程序。

错误模式如下:当 USB 串行设备插入并通电时,它会执行 POST 并将结果输出转储到串行端口上,其比特率与该端口的默认比特率完全不同。当稍后应用程序打开该 tty 端口时,它不会在打开时刷新。从那一刻起,仍在移位寄存器、FIFO 和 USB 缓冲区中累积的 POST 数据将被读取,并且将被回显,因为默认情况下回显已启用。由于比特率不匹配,回显的数据看起来像 NULL 字符或垃圾。

由于真正的解决方案是正确实现刷新,而且这可能很难甚至不可能,因此我使用这个简单的补丁为内核中的所有 USB UART 默认禁用了 termios echo:

    Index: drivers/usb/serial/usb-serial.c
===================================================================
--- drivers/usb/serial/usb-serial.c (revision 1166)
+++ drivers/usb/serial/usb-serial.c (working copy)
@@ -1270,6 +1270,8 @@
                            | HUPCL | CLOCAL;
    usb_serial_tty_driver->init_termios.c_ispeed = 9600;
    usb_serial_tty_driver->init_termios.c_ospeed = 9600;
+   usb_serial_tty_driver->init_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+   usb_serial_tty_driver->init_termios.c_oflag &= ~OPOST;
    tty_set_operations(usb_serial_tty_driver, &serial_ops);
    result = tty_register_driver(usb_serial_tty_driver);
    if (result) {
Run Code Online (Sandbox Code Playgroud)

这个补丁做了更多的事情;它将 tty 端口置于原始模式,因为我不再冒险。禁用 ICANON 和 ECHO 就足够了。

打上这个补丁后,打开时自发传输的数据就消失了。

打开后,我仍然收到打开前以空字符形式发送的 POST 数据。正如我所期望的,并不是在打开后立即,而是在发送第一个字节之后。这可能是由于驱动程序中的接收事件在打开之前被忽略,并且在打开后没有被触发,因为打开后没有收到任何内容。

由于我的应用程序在启动时忽略非成帧数据,因此我决定此补丁中的解决方案是此问题的解决方案。

我希望它也对其他人有帮助。