UDEV+USB设备检测异常问题求助
问题描述
在Yocto嵌入式Linux平台上,通过udev规则触发USB声卡插拔的音频采集脚本:
ACTION=="add", SUBSYSTEMS=="usb", DRIVERS=="snd-usb-audio", RUN+="/usr/bin/audio.sh start" ACTION=="remove", ENV{ID_MODEL}=="xxxxxx", RUN+="/usr/bin/audio.sh stop"
首次插拔设备完全正常,但再次连接设备后1分钟会出现udev worker超时警告:
udevd[128]: worker [559] /devices/platform/soc/2100000.bus/2184000.usb/ci_hdrc.0/usb1/1-1/1-1:1.0 is taking a long time
2分钟后出现脚本超时和USB设备控制读取失败日志:
[981.155879] udevd[559]: timeout '/usr/bin/audio.sh start' [984.571667] usb 1-1: cannot get ctl value: req = 0x81, wValue = 0x202, wIndex = 0x200, type = 4
此时断开设备,remove规则能正常触发;若提前断开,需等待worker超时才会执行remove逻辑。即使简化脚本为单命令,仍会出现worker被kill的错误,修改udev.conf的event_timeout为5秒也无效,更换设备问题依旧。
解决思路与方案
1. 绝对不要让udev直接运行阻塞/长时间进程
udev worker是同步执行的,会持有设备节点锁直到脚本结束。如果你的audio.sh start启动了持续运行的音频采集进程,udev worker会一直等待,最终触发超时。
解决方法:
- 在脚本末尾加
&让进程后台运行,同时重定向输出避免阻塞:# audio.sh start里的执行逻辑改成 /path/to/your/audio_capture >/dev/null 2>&1 & - 更可靠的方式是用systemd service替代直接RUN脚本:
- 创建
usb-audio-capture.service:[Unit] Description=USB Audio Capture Service BindsTo=sys-devices-platform-soc-2100000.bus-2184000.usb-ci_hdrc.0-usb1-1-1.device # 替换为你的设备路径 [Service] Type=simple ExecStart=/usr/bin/audio.sh start ExecStop=/usr/bin/audio.sh stop RemainAfterExit=no - 修改udev规则为启动/停止service:
ACTION=="add", SUBSYSTEMS=="usb", DRIVERS=="snd-usb-audio", RUN+="/usr/bin/systemctl start usb-audio-capture.service" ACTION=="remove", ENV{ID_MODEL}=="xxxxxx", RUN+="/usr/bin/systemctl stop usb-audio-capture.service"
- 创建
2. 检查脚本是否残留设备占用
日志里的cannot get ctl value是关键——这说明udev尝试读取USB设备控制信息时,设备被其他进程占用了。
- 确保
audio.sh stop能彻底杀死所有相关进程:# 不要只用pkill,精准匹配进程名或PID文件 PID=$(pgrep audio_capture) if [ -n "$PID" ]; then kill -9 "$PID" wait "$PID" 2>/dev/null fi - 首次插拔后,用
lsof /dev/snd/*检查设备节点是否被正确释放,再次插拔前确保无残留进程。
3. 优化udev规则的匹配精准度
当前add规则匹配SUBSYSTEMS=="usb",可能会触发多次(USB设备枚举时多个节点触发),导致重复执行脚本,进而占用设备。
- 改为匹配音频子系统,减少误触发:
ACTION=="add", SUBSYSTEM=="sound", DEVPATH=="*usb*", DRIVERS=="snd-usb-audio", RUN+="/usr/bin/audio.sh start &" - Remove规则用
ID_SERIAL替代ID_MODEL,避免同型号设备冲突,或者直接匹配设备路径:ACTION=="remove", DEVPATH=="/devices/platform/soc/2100000.bus/2184000.usb/ci_hdrc.0/usb1/1-1", RUN+="/usr/bin/audio.sh stop"
4. 正确设置udev超时(而非全局event_timeout)
修改udev.conf的event_timeout无效,是因为这个参数是全局事件队列超时,而worker的超时需要在规则里单独设置:
ACTION=="add", SUBSYSTEMS=="usb", DRIVERS=="snd-usb-audio", TIMEOUT="5", RUN+="/usr/bin/audio.sh start &"
或者修改udevd的启动参数,在systemd/system/udevd.service里添加--timeout=5:
ExecStart=/usr/lib/systemd/systemd-udevd --timeout=5
5. 排查USB内核驱动问题
如果更换多个设备都出现cannot get ctl value,可能是Yocto内核的USB驱动(比如ci_hdrc)存在bug:
- 升级Yocto的内核版本,或者检查是否有相关补丁(比如USB设备重新枚举时的资源泄漏补丁);
- 在内核配置里开启
CONFIG_USB_DEBUG,查看更详细的USB驱动日志,定位设备控制请求失败的原因。
内容的提问来源于stack exchange,提问作者tmakr
相关产品推荐
相关产品推荐

