[Bug Report] usb转串口异常输出
Tofloor
poster avatar
粘贴为纯文本
deepin
2026-10-16 10:23
Author

image.png

image.png

插拔usb设备会导致串口异常输出

设备

Bus 001 Device 011: ID 10c4:ea60 Silicon Labs CP210x UART Bridge

暂时应对

删除 /sbin/hwinfo就正常
sudo mv /sbin/hwinfo /sbin/hwinfo.bak

Reply Favorite View the author
All Replies
avatar
𰻞𰻝面
deepin
2026-10-16 14:17
#1

那是电流涌动,造成识别错误。我拿手摸TTL线也会在终端上显示出字符。

Reply View the author
avatar
粘贴为纯文本
deepin
2026-10-16 15:04
#2
𰻞𰻝面:

那是电流涌动,造成识别错误。我拿手摸TTL线也会在终端上显示出字符。

不是硬件干扰

Reply View the author
avatar
粘贴为纯文本
deepin
2026-10-16 15:11
#3

插拔 USB 后串口冒出 nn*n 乱码?根因找到了:设备管理器的 hwinfo 探测

一句话:deepin-devicemanager-server 每次 USB 插拔都会执行 hwinfo --usb --keyboard --mouse,而 hwinfo 的串口鼠标探测会把你正在用的串口强行改成 1200/7N1,并往里写 4 遍字符串 "*n"(正好 8 字节)。这就是乱码的来源。

这不是新问题,GitHub issue #6921(2024-01)报过同一件事,当年给了补丁但没修好(原因见下),至今仍是 open。

【现象】

picocom 连着开发板(115200 8N1),每次插上/拔掉任意 USB 设备(U 盘、鼠标、另一个串口线都算),窗口里就闪出一串重复字符:

*n*n*n*n

删掉/改名 hwinfo 就正常。

【根因:三段代码】

谁在调用(deepin-devicemanager 源码)

/* src/hotplug/detectthread.cpp:107 */
process.start("hwinfo", QStringList() << "--usb" << "--keyboard" << "--mouse");

由 monitorusb.cpp 的 libudev 监听触发,add/remove 事件即执行。关键是带了 --mouse。

hwinfo 拿着 --mouse 干了什么(hwinfo 源码)

/* src/hd/mouse.c:389-397 */
if( hd->sub_class.id == sc_com_ser &&     // 是串口(每个 ttyUSB* 都被这么标)
    !hd->tag.skip_mouse ) {
  fd = open(hd->unix_dev_name, O_RDWR | O_NONBLOCK);   // 抢你的口,不看锁文件
  setspeed(fd, 1200, 1, CS7);                          // :410 强制 1200 / 7 位数据位

写进去的就是 "*n" 这两个字符

/* src/hd/mouse.c:558 / :562 */
case 1200: default: c = "*n";  tty.c_cflag = flags | B1200;  break;
if(needtowrite) { write(fd, c, 2); }        // 把 "*n" 写进你的串口

/* src/hd/mouse.c:573 */
for(i = 9600; i >= 1200; i >>= 1)           // 4 轮循环
  _setspeed(fd, i, new, needtowrite, flags);

循环 4 轮、每轮 2 字节 —— 所以屏幕上是同一个图案重复,而不是随机乱码。

注:这段代码的注释写着 "Baud setting magic taken from gpm.",是控制台鼠标守护 gpm 的老逻辑,*n 本来是 MouseSystems 协议问鼠标型号的指令。对着不是鼠标的串口发,就是纯破坏。

【实锤:8 = 4 × 2】

issue #6921 里报告人量到的是「拔插 USB 后 /proc/tty/driver/serial 的 tx 值加 8」。

4 轮循环 × 每轮 write(fd, "*n", 2) = 8 字节

两个不同的人、两台机器、相隔一年多,测出来的数字和源码逐字节吻合。

顺带解释当年那个补丁为什么"没屏蔽全":补丁只注释掉了 _setspeed() 里的 tcsetattr(不改波特率了),但​没动紧跟其后的那个 write​,8 字节照发、tx 照 +8。

【为什么有的串口没事(别高兴太早)】

hwinfo 的 usb.c:957-963 里写死了一条豁免:

// bnc #408715 (T-Balancer BigNG)
if(vendor == 0x0403 && device == 0x6001)   // = FT232R 芯片
  skip_mouse = skip_modem = 1;

这是 2008 年为保护一个 FTDI 芯片的水冷控制器打的补丁(openSUSE changelog: "Mon Jul 21 2008 skip T-Balancer BigNG")。它按芯片 ID 匹配,于是误保护了所有 FT232R 设备,而 CP2102 / CH340 等一律不豁免。

注意:这条豁免不覆盖 braille,也没法用 udev / 环境变量改(编译进二进制的逻辑)。

【修复:给 hwinfo 加 hwprobe(推荐)】

hwinfo 有官方开关 hwprobe,官方文档写明它 "cannot be overridden by command line switches" —— 也就是说设备管理器传 --mouse 也压不过它。

sudo dpkg-divert --rename --divert /usr/sbin/hwinfo.real --add /usr/sbin/hwinfo
sudo tee /usr/sbin/hwinfo >/dev/null <<'EOF'
#!/bin/sh
: "${hwprobe:=-mouse,-modem,-braille}"
export hwprobe
exec /usr/sbin/hwinfo.real "$@"
EOF
sudo chmod 755 /usr/sbin/hwinfo

用 dpkg-divert 是故意的:以后 hwinfo 升级,新二进制会装到 hwinfo.real,wrapper 不会被冲掉。
撤销:sudo dpkg-divert --remove --rename /usr/sbin/hwinfo

想全局覆盖(需重启)也可以改内核命令行加 hwprobe=-mouse,-modem,-braille。

副作用:设备管理器的"鼠标"页信息可能变少(有 lshw/sysfs 兜底)。对开发机来说这个代价很划算。

不建议:直接删 hwinfo(会连带废掉设备管理器的 USB/鼠标/键盘信息,且 apt 一升级就装回来)。

【验证(30 秒)】

# 改之前:能看到往非 FT232R 的口写 "*n"
sudo strace -f -e trace=openat,write -s 16 /usr/sbin/hwinfo --mouse 2>&1 | grep -E 'ttyUSB|"\*n"'

# 改之后:应该完全无输出
sudo strace -f -e trace=openat,write -s 16 /usr/sbin/hwinfo --usb --keyboard --mouse 2>&1 | grep -E 'ttyUSB|"\*n"'

【给官方】

  1. detectthread.cpp:107 那行的 --mouse 建议去掉(识别 USB 设备用 --usb 就够,且 --usb 不碰串口),或者给 hwinfo 调用统一加 hwprobe=-mouse,-modem,-braille。
  2. 用一个会 open(O_RDWR) 并改写串口 termios 的工具做设备信息采集,本身就不安全。

参考:
· issue #6921 https://github.com/linuxdeepin/developer-center/issues/6921
· 论坛旧帖 2025-01 https://bbs.deepin.org/post/283222
· hwinfo 源码 https://github.com/openSUSE/hwinfo
· deepin-devicemanager 源码 https://github.com/linuxdeepin/deepin-devicemanager

Reply View the author