USB Host口设备枚举失败的一种异常处理方法

问题描述

USB Host 口接入某些 USB 设备时会出现小概率枚举失败。没有找到根本原因。可以想办法规避,主机软件给 USB 端口重上电就能重新枚举成功。但是在应用层想通过 libusb 库来判断枚举失败存在如下问题:

  1. 应用层检测到枚举异常并重上电这个过程太长了,长达1分多钟,这个过程从指从插入 USB 设备到应用层感知到枚举失败这一段时间,时间长是因为内核枚举过程太长,应用层收到内核枚举失败的消息等太久

  2. 有时枚举失败,内核不会向应用层上报消息,这种情况应用层根本就不知道设备枚举失败了
    要解决上述两个问题。

分析过程

研究内核,libusb,libudev/systemd-udev 通信机制。

USB 设备的枚举过程为,在设备级别(指设备、配置、接口、端点术语中的设备)枚举完成,内核发现新设备时刻(此时接口级别驱动未加载,设备还不可用),内核向用户空间 systemd-udev 守护进程发送设备热插拔消息,systemd-udev 收到消息会在 /dev 目录创建设备节点,并进行一些配置,设备配置完成后,systemd-udev 向 libusb 发送热插拔消息( libusb 实际调用 libudev api 订阅来自 systemd-udev 的设备热插拔消息),libusb 通知应用层程序,此时此 USB 设备才变为可用状态。

USB 的设备、配置、接口、端点等概念可参考“USB工程应用基础概念简介”第 4 节内容。

解决方法

基于 libudev api(未使用 libusb库 ) 实现一个设备 monitor,直接向内核订阅 usb 设备热插拔消息,并同时启动定时器,定时器超时仍未收到 libusb上报新设备,则认为此次枚举失败。给 vbus 断电半秒再上电(vbus 断电就是设备断电),触发一次新枚举过程。

知识扩展

udev

udev 在用户空间管理内核设备,u 是 usersapce(用户空间)的缩写。与 udev 相关的有三个组件:udevd 守护进程(systemd-udev),udev 管理工具 udevadm,libudev 库。经查,systemd-udev 并未使用 libudev 库。

其中,systemd-udev 以守护进程的方式运行于 Linux 系统,并监听在新设备初始化或设备从系统中移除时,内核通过 netlink socket 所发出的 uevent。内核在 sysfs 创建删除一个设备之后,会通过 netlink socket 发送 uevent 通知 udevd,udevd 守护进程(systemd-udev)收到 sysfs 传过来的 uevent 后,在 /dev/ 目录创建或者删除设备节点。

udevadm 是一个命令,可管理 udev 运行行为,处理内核事件,提供简单的调试机制等。

libudev 库封装了 netlink socket 的通信,提供一组 api 供应用程序调用,这样应用程序能监控到设备插拔等事件。经查,systemd-udev 和 udevadm 都没使用 libudev 库。libusb 则视系统是否安装了 libudev 来决定是否使用它。

libusb

内核在 sysfs 中创建移除设备,udevd 在 dev 中创建移除设备,内核通过 netlink socket 向用户空间(udevd 等)进程发送 uevent 来通知设备插拔事件。当系统中安装有 libudev 库时,libusb 使用 libudev 来监听设备插拔事件,若没有 libudev 库,libusb 将使用 netlink socket 通信方式来监听设备插拔事件。

posted @ 2025-06-19 20:35  叶余  阅读(196)  评论(0)    收藏  举报