USB Gadget设备软插拔异常的处理方法

技术背景

我们的板子作为 USB Gadget 设备通过 USB 线接入 USB 主机使用,我们的板子被主机识别为一个 Compsite Device,这个 Compsite Device 是由我们板子根据 Host 口实际接的 USB 设备动态创建的,所以它包含哪些功能,由接在 Host 口的设备决定。为方便描述,把这个 Compsite Device 称作 USB Gadget 设备。

当板子 Host 口上接的 USB 设备有变化时,比如拔掉一个键盘,或者新插入一个鼠标,这时 USB Gadget 设备都要被销毁重建,因为设备功能变化了。

问题描述

  1. 我们需要实现一个类似这样的功能:不插拔我们板子与电脑之前的 USB 连线,通过软件开关来实现相当于 USB 线插拔的功能,把我们板子与电脑用 USB 线提前连好,在我们板上执行一个连接,电脑上显示有新设备插入,再执行一下断开连接命令,电脑上显示设备被移除。为了方便,暂称这个功能为软插拔功能吧。

  2. 另外,发现一个问题,就是我们板子内创建的 USB Gadget 设备在销毁后,电脑侧无法感知到这个设备已被销毁,再次重新创建一个 USB Gadget 设备,电脑无法识别这个设备,设备枚举会失败。也就是 USB Gadget 设备无法重复创建,而它本身是需要动态创建的。

最后分析发现,上述这两点,实际是由同一个原因引起的。

分析过程

研究电脑对 USB 线插拔的检测机制,研究我们板上使用的 USB 控制器和 USB PHY 是否有寄存器等去配置来产生类似于 USB 线插拔的硬件行为。

原因解析

设备插拔的硬件检测机制:在 USB Hub 的每个下游端口的 D+ 和 D- 引脚上,分别接了一个 15K 的下拉电阻,当 USB Hub 下游端口未接设备时,因下拉电阻作用,D+ 和 D- 就是低电平。而在 USB 设备端,则在 D+ 或 D- 引脚上接了 1.5K 的上拉电阻。USB 1.1 和 USB 2.0 设备此上拉电阻是接在 D+ 上,USB 1.0 设备此上拉电阻是接在 D- 上。这样,当设备插入到 Hub 时,由 1.5K 的上拉电阻和 15K 的下拉电阻分压,其中一条信号线(D+ 或 D-)就会被拉高。HUB 检测到 D+ 或 D- 引脚上电平变换后,就知道有设备插入或移除。当 D+ 出现一个上升沿,表示新插入了一个 USB 1.1 或 USB 2.0 的设备,当 D- 出现一个上升沿,表示新插入了一个 USB 1.0 的设备;当 D+ 出现一个下降沿,表示拔除了一个 USB 1.1 或 USB 2.0 的设备,当 D- 出现一个下降沿,表示拔除了一个 USB 1.0 的设备。HUB 会将这个插拔信号上报到上游端口,Hub 层层上报,到达 Root Hub,Root Hub 上报给 USB 主机控制机,主机就检测到了插拔机制。

阅读 USB PHY 芯片手册,发现手册中有提供寄存器,可以控制 D+/D-上的上拉电阻打开或关闭,所以软插拔功能理论上是行得通的,之所以还有问题,应该是内核中 USB 驱动有 BUG。我们的 USB Gadget 设备是 USB 2.0 设备,插拔 USB 线时,应该在 D+ 上出现上升沿和下降沿,实测在销毁 USB Gadget 设备后,D+ 没有从高电平变为低电平,即 D+ 未能成功关闭上拉电阻。不能稳定地使能/禁止 D+ 脚上拉,就是USB Gadget 设备第二次创建后 PC 不能识别的原因,也是软插拔功能不工作的原因。

继续往下排查,找内核中偏硬件底层与上拉控制有关的代码痕迹,找到 gadget.c 中 dwc3_gadget_pullup(),这个函数嫌弃较大。仔细阅读代码,发现在 dwc3_gadget_soft_disconnect() 在 dwc3_gadget_soft_connect() 之前有一个 dwc3_gadget_soft_reset(),阅读我们板 USB 控制器的手册中 DCTL (USB3_XHCI) 寄存器的描述,提到 "Once DCTL.CSFRST bit is cleared, the software must wait at least 3 PHY clocks before accessing the PHY domain (synchronization delay). Waiting 5ms is long enough here. ",dwc3_gadget_soft_reset() 针对我们的 USB 控制器型号没有延时,在此函数中增加 5 ms 延时,问题解决。再用万用表测一下,销毁和重建 USB Gadget 设备时,D+ 可以按预期变化了,将 USB 接上电脑,功能也正常了。至此问题解决。

继续阅读下代码,发现 drivers/usb/gadget/udc/core.c 中内核通过 sysfs 接口向用户空间提供了connectdisconnect两个命令,这两个命令的实现在 soft_connect_store() 函数中,分别调用了两个函数:usb_gadget_connect() 和 usb_gadget_disconnect(),它们最终都是通过控制 D+/D- 的上拉电阻来实际设备与主机间的 connect 和 disconnect。所以 USB Gadget 的软插拔功能在内核中是有实现的,只是刚好在我们的硬件平台上有 BUG。

解决方法

问题描述中的两个问题实际是同一个原因引起。如原因分析中所述,在 dwc3_gadget_soft_reset() 中增加延时,这两个问题即解决。

参考资料

1. Windows环境下USB设备的插入检测机制

相关推荐
叶余14 小时前
USB Gadget设备枚举失败的处理方法
usb·hid·gadget
叶余3 天前
USBIP技术简介
usb·usbip
叶余5 天前
USB工程应用基础概念简介
usb
zgyrelax_zgykill18 天前
USB子系统和type-c接口快速理解
usb·typec
读书札记20222 个月前
Windows 查看电脑是否插拔过U盘
windows·电脑·usb
无聊到发博客的菜鸟2 个月前
STM32实现SPI转USB虚拟串口输出(实测40M时钟不丢包)
stm32·嵌入式·usb·spi·虚拟串口
ansondroider2 个月前
Android RK356X TVSettings USB调试开关
android·adb·usb·otg·rk356x
正点原子2 个月前
【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——USB测试 #USB HOST #USB 鼠标
linux·功能测试·stm32·嵌入式硬件·计算机外设·usb
打工人你好3 个月前
USB(通用串行总线)数据传输机制和包结构简介
usb