1. 从一次真实的USB设备“失联”说起
上周三凌晨一点,实验室的工控机突然读不到那台用了三年的周立功USBCANFD接口卡了。dmesg里刷了一屏的device descriptor read/64, error -71,插拔了七八次,换了三个USB口,甚至把机箱后面板所有接口都试了一遍,设备依然像人间蒸发一样。当时第一反应是驱动崩了,重装libusb、重编内核模块,折腾了快两个小时,最后用lsusb一敲,发现设备压根没出现在总线上——问题根本不在驱动层,而是USB物理链路或者供电出了岔子。
这件事让我意识到一个很普遍的现象:很多人遇到Linux下USB设备识别不了,第一反应就是去查驱动、翻内核日志、重装系统,却忽略了最基础也最有效的一步——先用lsusb把问题范围框定下来。lsusb这个命令看起来简单,但它能帮你把“设备到底有没有被系统看到”这件事说清楚,而这一步恰恰是排查所有USB问题的起点。
这篇文章就是围绕lsusb这个核心工具,把Linux下USB设备识别失败的排查流程完整拆一遍。不管你是插了USB转串口模块没反应,还是STM32开发板连上电脑后/dev/ttyUSB0死活不出来,又或者是USB Blaster、USBCANFD接口卡这类专业设备突然罢工,这套流程都能帮你快速定位问题出在哪一层。内容会从USB协议的基础逻辑讲起,再到lsusb的详细用法、输出解读、常见故障分类排查,最后给出一套可以直接照着走的排查清单。适合有一定Linux基础、平时需要跟各种USB外设打交道的开发和运维人员参考。
2. 为什么lsusb是USB排查的第一入口
2.1 USB设备的枚举过程与lsusb的定位
要理解lsusb为什么这么关键,得先搞清楚Linux系统是怎么“看见”一个USB设备的。当你把设备插进USB口的那一刻,系统内部发生了一系列标准化的枚举流程:主机控制器检测到端口电平变化,给设备供电,然后发起复位操作,接着通过控制端点0读取设备描述符,分配地址,再读取配置描述符、接口描述符、端点描述符,最后根据设备类代码加载对应的驱动。整个过程走完,设备才算真正“上线”。
lsusb做的事情,就是直接读取/sys/bus/usb/devices/和/dev/bus/usb/下的信息,把当前总线上已经完成枚举的设备列出来。换句话说,只要lsusb能看到设备,就说明USB物理层、电气层、协议层的枚举至少走通了大部分;如果lsusb看不到,那问题一定出在枚举完成之前,跟内核驱动、文件系统、应用程序都没关系。
这个判断逻辑非常重要。很多人一上来就dmesg | grep usb,看到一堆报错就慌了,但其实dmesg里的信息量太大,有控制器初始化的、有枚举失败的、有驱动绑定的,混在一起很难快速定位。而lsusb给出的是一份“当前在线设备清单”,干净利落,一眼就能判断设备到底有没有被系统认到。
2.2 lsusb与dmesg、/sys、/dev的分工
在实际排查中,lsusb不是孤立的,它和另外几个信息源配合使用才能发挥最大价值。我习惯把它们的分工这样理解:
| 信息源 | 回答的问题 | 使用时机 |
|---|---|---|
lsusb | 设备有没有被系统枚举到 | 第一步,判断问题层级 |
dmesg | 枚举过程中发生了什么错误 | 第二步,看具体失败原因 |
/sys/bus/usb/devices/ | 设备的详细描述符和拓扑 | 第三步,深入分析设备能力 |
/dev/ | 设备节点有没有生成 | 第四步,确认用户空间可用性 |
这个顺序很关键。先用lsusb确认设备是否在线,如果在线但功能不正常,再去dmesg看驱动绑定情况;如果不在线,直接跳到物理层和供电层排查。这样一层层往下走,不会像无头苍蝇一样乱撞。
2.3 一个容易被忽略的细节:lsusb看到的不等于能用
这里要特别提醒一点:lsusb能看到设备,只代表USB枚举完成了,不代表驱动加载成功,更不代表应用程序能正常使用。我见过太多案例,lsusb里设备赫然在列,但/dev/ttyUSB0就是不出来,或者出来了但一读就报Input/output error。这种情况问题往往出在驱动层或者设备固件层,需要结合dmesg和/sys继续深挖。
反过来,如果lsusb看不到设备,那就不用浪费时间查驱动了,直接去查线缆、供电、端口、BIOS设置这些底层问题。这个判断能帮你省下大量无效折腾的时间。
3. lsusb命令的完整用法与输出解读
3.1 基础用法与常用参数
lsusb的基本用法非常简单,直接敲命令就行:
lsusb输出大概长这样:
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 003: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC Bus 001 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub每一行的结构是:Bus XXX Device YYY: ID VVVV:PPPP 厂商描述。其中VVVV是厂商ID(VID),PPPP是产品ID(PID),这两个是识别设备身份的核心标识。
常用的参数有这么几个:
lsusb -t:以树形结构显示USB拓扑,能清楚看到设备挂在哪个控制器、哪个Hub下面,排查端口问题时特别有用。lsusb -v:输出设备的完整描述符信息,包括配置、接口、端点、设备类、供电参数等,信息量极大,通常配合-d指定具体设备使用。lsusb -d VVVV:PPPP:只看指定VID:PID的设备,比如lsusb -d 0403:6001只看FT232芯片的设备。lsusb -s Bus:Device:只看指定总线号和设备号的设备,比如lsusb -s 001:003。lsusb -D /dev/bus/usb/001/003:直接读取指定设备节点的描述符,适合设备节点存在但lsusb列表里看不到的诡异情况。
我平时排查用得最多的是lsusb和lsusb -t这两个,前者看设备清单,后者看拓扑关系。-v参数输出太长,一般只在需要确认设备类代码或者端点配置时才用。
3.2 输出信息逐字段拆解
拿上面那行FT232设备举例:
Bus 001 Device 003: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) ICBus 001:设备挂在第1号USB总线上。Linux系统里每条USB总线对应一个主机控制器,总线号在系统启动时分配。Device 003:这是设备在总线上的地址,每次插拔都会重新分配,所以同一个设备在不同时间插可能显示不同的Device号。ID 0403:6001:0403是FTDI公司的VID,6001是FT232R芯片的PID。这两个号码是USB-IF分配给厂商的,全球唯一。Future Technology Devices International, Ltd:厂商名称,来自设备描述符里的字符串描述符。FT232 Serial (UART) IC:产品名称,同样来自设备描述符。
这里有个经验点:如果厂商名称和产品名称显示为空白或者乱码,往往说明设备描述符读取不完整,可能是固件有问题或者供电不稳导致枚举中断。这种情况即使lsusb能看到设备,后续功能也大概率不正常。
3.3 用lsusb -t看拓扑结构
lsusb -t的输出是这样的:
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/16p, 480M |__ Port 5: Dev 3, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 12M |__ Port 7: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 12M这个视图能直观看到几件事:设备挂在哪个总线的哪个端口上、走的什么速度(12M是全速,480M是高速,5000M是超高速)、绑定了什么驱动。排查“设备插在Hub上不识别”这类问题时,lsusb -t能帮你确认设备到底有没有出现在拓扑里,以及它协商到的速度是否正常。
我遇到过一种情况:USB 3.0的移动硬盘插在USB 2.0的Hub上,lsusb能看到设备,但lsusb -t显示速度只有480M,而且设备频繁掉线。后来换到直连主板的USB 3.0口就正常了。这就是拓扑和速度协商的问题,光看lsusb列表是发现不了的。
3.4 用lsusb -v深挖设备描述符
当你需要确认设备的类代码、端点配置、供电需求时,lsusb -v是唯一的选择。比如查一个USB转串口设备:
lsusb -v -d 0403:6001输出里重点看这几段:
bDeviceClass:设备类代码。00表示由接口定义类,02是通信类,08是大容量存储类,FF是厂商自定义。bNumConfigurations:配置数量,通常是1。bNumInterfaces:接口数量。USB转串口设备通常有1个接口,复合设备可能有多个。bNumEndpoints:端点数量。串口设备一般有3个端点:控制、批量输入、批量输出。bMaxPower:设备从总线取的最大电流,单位是2mA。比如bMaxPower 100mA表示取200mA。
这里有个很实用的排查点:如果bMaxPower显示的值超过了你所接端口的供电能力,设备就可能因为供电不足而枚举失败。特别是USB 2.0端口标准供电是500mA,如果设备要900mA,插在USB 2.0口上就可能出问题,必须换USB 3.0口或者带外部供电的Hub。
4. USB设备识别失败的分类排查流程
4.1 第一层:lsusb完全看不到设备
这是最常见也最让人头疼的情况。设备插上去,lsusb列表里完全没有新增行,dmesg里可能连枚举开始的日志都没有。问题一定出在枚举完成之前,按以下顺序排查:
物理连接检查。先换一根USB线。别觉得这是废话,我踩过至少三次坑都是线的问题:有一次是线内部断了D+或D-,供电正常但数据不通;还有一次是劣质线缆导致信号完整性太差,低速设备能认,高速设备就枚举失败。换线之后问题直接消失。另外检查接口有没有氧化、松动,特别是工控环境里用了两三年的机器,USB口积灰导致接触不良很常见。
端口和Hub排查。把设备从Hub上拔下来,直连主板后置USB口。很多前置面板的USB口是通过排线接到主板针脚上的,排线质量差或者没插紧都会导致设备不识别。如果是笔记本,换另一侧的USB口试试,有些笔记本的USB口是挂在不同控制器下的,供电能力也不一样。
供电问题确认。大功率设备(移动硬盘、USBCANFD接口卡、带外部供电的USB Hub)对电流要求高。用lsusb -v看设备的bMaxPower,如果超过500mA,必须接外部供电或者换USB 3.0口。有个简单的判断方法:插上设备后摸一下设备外壳或者接口附近,如果明显发热但系统不认,大概率是供电不足导致枚举反复重启。
BIOS/UEFI设置检查。有些工控机或者服务器默认关闭了USB控制器,或者把USB模式设成了“仅键盘鼠标”。进BIOS看USB Configuration里的USB Controller、XHCI Hand-off、Legacy USB Support这些选项,确保USB控制器是启用的。虚拟机里跑Linux的话,还要确认USB设备有没有从宿主机直通给虚拟机。
内核模块检查。确认xhci_hcd、ehci_hcd、ohci_hcd、uhci_hcd这些主机控制器驱动有没有加载:
lsmod | grep -E "xhci|ehci|ohci|uhci"如果全都没有,说明USB控制器驱动没加载,lsusb自然什么都看不到。这种情况需要检查内核配置或者重新加载模块。
4.2 第二层:lsusb能看到但设备节点不生成
lsusb里设备在列,但/dev/ttyUSB0、/dev/ttyACM0这些设备节点死活不出来。问题出在驱动绑定阶段,排查思路如下:
确认驱动是否匹配。用lsusb -t看设备的Driver=字段。如果显示Driver=(none)或者空白,说明没有驱动绑定。这时候需要确认内核有没有编译对应的驱动模块。比如FT232R需要ftdi_sio模块,CH340需要ch341模块,CP210x需要cp210x模块。
modprobe ftdi_sio modprobe ch341 modprobe cp210x加载后再看lsusb -t,如果驱动绑上了,设备节点通常就会自动生成。
检查VID:PID是否在驱动支持列表里。有些国产USB转串口芯片用的是厂商自定义的VID:PID,标准驱动不认。比如某些CH340的变种芯片,PID不是7523而是其他值,ch341驱动就不会自动绑定。解决方法是用new_id手动添加:
echo "1a86 5523" > /sys/bus/usb-serial/drivers/ch341/new_id这个操作需要root权限,而且重启后失效,要持久化得写进/etc/udev/rules.d/下的规则文件。
看dmesg里的驱动绑定日志。dmesg | tail -50,重点找usb-serial、ttyUSB、ttyACM相关的行。如果看到usb 1-5: ch341-uart converter now attached to ttyUSB0,说明驱动绑定成功,设备节点应该已经生成了。如果看到probe failed或者device not accepting address,那就是驱动探测失败,需要进一步分析。
权限问题排查。设备节点生成了,但普通用户访问不了,表现为Permission denied。用ls -l /dev/ttyUSB0看权限,通常是crw-rw---- root dialout。把当前用户加入dialout组:
sudo usermod -aG dialout $USER然后重新登录生效。这个坑很常见,特别是Ubuntu新版本默认把用户放在dialout组外面。
4.3 第三层:设备节点存在但功能异常
设备节点有了,但一读写就报错,或者数据乱码、频繁掉线。这类问题往往跟供电、固件、协议配置有关:
供电稳定性排查。用dmesg -w实时监控,插上设备后观察有没有device descriptor read/64, error -71、reset full-speed USB device、usb 1-5: USB disconnect这类反复出现的日志。如果有,基本可以确定是供电或者信号完整性问题。换带外部供电的Hub,或者换一根带磁环的屏蔽线,往往能解决。
波特率与流控配置。USB转串口设备在Linux下用stty配置波特率:
stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb如果波特率不匹配,读出来的就是乱码。另外注意硬件流控(RTS/CTS)有没有正确配置,有些设备需要流控才能正常通信。
固件版本确认。某些USB设备(比如FTDI的FT232R)有EEPROM固件,如果固件损坏或者配置错误,设备能枚举但功能不正常。用lsusb -v看iSerial、iProduct这些字符串描述符是否正常,如果显示异常,可能需要用厂商工具重新烧录固件。
USB协议分析。如果以上都排查了还是有问题,就需要上USB抓包工具了。Linux下可以用usbmon内核模块配合tcpdump或者Wireshark抓USB流量:
modprobe usbmon ls /sys/kernel/debug/usb/usbmon/然后tcpdump -i usbmon1 -w usb.pcap抓包,用Wireshark分析。这个手段比较重,一般只在驱动开发或者协议调试时用。
4.4 第四层:特定设备的特殊问题
不同类型的USB设备有各自的“脾气”,这里列几个常见的:
| 设备类型 | 典型问题 | 排查重点 |
|---|---|---|
| USB转串口(FT232/CH340/CP210x) | 驱动不绑定、设备节点不生成 | 确认VID:PID、加载对应模块、检查new_id |
| USB Blaster(FPGA下载器) | 权限不足、udev规则缺失 | 添加udev规则、确认usbblaster驱动 |
| USBCANFD接口卡 | 供电不足、驱动版本不匹配 | 外部供电、厂商驱动版本确认 |
| STM32开发板USB | 枚举失败、DFU模式识别 | 检查BOOT引脚、确认DFU驱动 |
| USB摄像头 | 带宽不足、格式不支持 | 换USB控制器、确认UVC驱动 |
| USB存储设备 | 分区表损坏、文件系统不识别 | lsblk、fdisk -l、fsck |
拿USB Blaster举例,这是Altera FPGA下载器,在Linux下需要usbblaster驱动和对应的udev规则。如果规则没配好,普通用户没权限访问,Quartus就认不到下载器。解决方法是创建/etc/udev/rules.d/51-usbblaster.rules:
SUBSYSTEM=="usb", ATTR{idVendor}=="09fb", ATTR{idProduct}=="6001", MODE="0666"然后udevadm control --reload-rules重新加载。
5. 一套可以直接抄的排查清单
5.1 五分钟快速定位流程
把上面的排查逻辑浓缩成一个可以照着走的流程:
第1分钟:确认设备是否被枚举
lsusb lsusb -t对比插拔前后的输出,看设备有没有出现。如果出现了,记下Bus号和Device号,进入第3分钟。如果没出现,进入第2分钟。
第2分钟:物理层和供电排查
- 换USB线、换端口、直连主板
- 检查
dmesg | tail -30有没有枚举错误 - 确认BIOS里USB控制器启用
- 大功率设备接外部供电
第3分钟:驱动绑定确认
lsusb -t | grep -A2 "Bus" dmesg | grep -E "usb|tty"看Driver=字段有没有绑定,看dmesg里有没有now attached to ttyUSBx。
第4分钟:设备节点和权限
ls -l /dev/ttyUSB* /dev/ttyACM* groups确认节点存在、权限正确、用户在dialout组。
第5分钟:功能验证
stty -F /dev/ttyUSB0 115200 cat /dev/ttyUSB0或者用minicom、screen、picocom做实际通信测试。
5.2 常见错误信息速查表
| dmesg错误信息 | 含义 | 解决方向 |
|---|---|---|
device descriptor read/64, error -71 | 枚举时读取描述符失败 | 供电、线缆、端口 |
device not accepting address | 设备不接受分配的地址 | 供电、固件、控制器兼容性 |
unable to enumerate USB device | 枚举彻底失败 | 物理层、供电、BIOS设置 |
usb 1-5: reset full-speed USB device | 设备反复复位 | 供电不稳、线缆质量差 |
probe of 1-5:1.0 failed with error -5 | 驱动探测失败 | 驱动不匹配、固件问题 |
ch341-uart converter now attached to ttyUSB0 | 驱动绑定成功 | 正常,检查权限 |
usbfs: interface 0 claimed by ftdi_sio | 接口被驱动占用 | 正常,说明驱动已绑定 |
5.3 几个我踩过的坑和对应技巧
坑一:虚拟机USB直通后宿主机抢设备。在VMware或VirtualBox里把USB设备直通给Linux虚拟机,结果宿主机又自动挂载了。解决方法是先在宿主机里把设备断开,再在虚拟机设置里勾选“显示所有USB设备”,然后手动连接。VirtualBox还需要装Extension Pack才能支持USB 2.0/3.0。
坑二:USB 3.0设备插在USB 2.0口上枚举失败。有些USB 3.0设备对供电要求高,插在USB 2.0口上虽然lsusb能看到,但速度协商失败,设备反复掉线。lsusb -t里会显示480M而不是5000M。换USB 3.0口即可。
坑三:国产USB转串口芯片VID:PID不标准。某些CH340模块的PID是5523而不是标准的7523,ch341驱动不自动绑定。用new_id手动添加后正常。这个技巧在工控现场特别实用,因为很多国产模块为了规避驱动冲突会改PID。
坑四:udev规则写错导致设备节点权限不对。写udev规则时ATTR{idVendor}和ATTR{idProduct}的匹配要精确,大小写敏感。规则文件放/etc/udev/rules.d/下,文件名以数字开头,比如99-mydevice.rules。改完记得udevadm control --reload-rules && udevadm trigger。
坑五:lsusb看到设备但dmesg里没有任何驱动日志。这种情况往往是设备描述符读取不完整,系统认到了设备但无法识别它的类代码。用lsusb -v -d VVVV:PPPP看bDeviceClass和bInterfaceClass,如果是00或者FF,说明设备需要厂商自定义驱动。这时候要么装厂商驱动,要么用libusb写用户态程序直接操作。
6. 从lsusb延伸出去的几个实用技巧
6.1 用udev规则固化设备节点名
USB转串口设备插拔后设备节点号会变,今天ttyUSB0明天可能变ttyUSB1,写脚本时很麻烦。用udev规则可以根据VID:PID和序列号固定节点名:
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A50285BI", SYMLINK+="ttyFT232"这样无论插拔多少次,/dev/ttyFT232始终指向这个设备。序列号用lsusb -v -d 0403:6001 | grep iSerial查。
6.2 用usbmon做协议级抓包
前面提过usbmon,这里补充一下具体用法。加载模块后,/sys/kernel/debug/usb/usbmon/下会有0u、1u、2u等文件,对应不同总线。用cat直接读或者用tcpdump抓:
cat /sys/kernel/debug/usb/usbmon/1u > usb.log输出是文本格式的USB事务记录,能看到控制传输、批量传输的原始数据。分析枚举失败时特别有用,能看到具体是在哪个描述符读取阶段出的错。
6.3 用lsusb配合脚本做设备监控
写个简单的监控脚本,设备插拔时自动记录:
#!/bin/bash while true; do lsusb > /tmp/usb_now.txt if ! diff -q /tmp/usb_last.txt /tmp/usb_now.txt > /dev/null 2>&1; then echo "USB设备变化:$(date)" diff /tmp/usb_last.txt /tmp/usb_now.txt cp /tmp/usb_now.txt /tmp/usb_last.txt fi sleep 2 done这个脚本在调试频繁插拔的设备时很好用,能自动记录每次变化的时间点和设备差异。
6.4 关于USB总线频率和设备兼容性
USB 2.0的全速模式是12Mbps,高速模式是480Mbps;USB 3.0是5Gbps,USB 3.1是10Gbps。有些老设备只支持低速(1.5Mbps)或全速,插在USB 3.0口上可能因为控制器兼容性问题枚举失败。lsusb -t里能看到每个设备协商到的速度。如果发现设备协商速度异常低,可以尝试在BIOS里关闭XHCI Hand-off,或者把设备插到USB 2.0口上。
另外,USB Hub的级联层数也有限制,规范最多7层。超过7层会导致枚举失败。工控现场如果用了多级Hub,要确认拓扑深度没有超标。
7. 写在最后的一些个人体会
排查USB设备识别问题这件事,说到底就是一层层剥洋葱:先用lsusb确认设备有没有被系统看到,再根据看到与否决定往哪个方向深挖。这个判断逻辑看起来简单,但实际工作中能帮你省下大量无效折腾的时间。我见过太多人一上来就重装驱动、重编内核,结果折腾半天发现是USB线坏了。
lsusb这个命令本身没什么复杂的,但它的价值在于给你一个清晰的起点。配合dmesg、lsusb -t、/sys这些信息源,基本上90%以上的USB识别问题都能在五分钟内定位到大致方向。剩下的就是根据方向去具体排查,该换线的换线,该加驱动的加驱动,该写udev规则的写udev规则。
最后分享一个小习惯:我在每台经常接USB外设的Linux机器上都会放一个usb-debug.sh脚本,里面就是前面那套五分钟排查流程的自动化版本,插上设备后一键跑一遍,输出lsusb、lsusb -t、dmesg | tail -50、/dev/tty*列表和权限信息。这样即使半夜被叫起来处理问题,也能快速进入状态,不用从头回忆命令。这个脚本后来在团队里传开了,大家都觉得比翻文档快得多。