技术背景

我们的板子作为 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设备的插入检测机制

USB Gadget设备软插拔异常的处理方法的更多相关文章

  1. C# 查询所有设备的插拔事件

    private void test() { //Win32_DeviceChangeEvent  Win32_VolumeChangeEvent ManagementEventWatcher watc ...

  2. 2018-8-10-WPF-判断USB插拔

    title author date CreateTime categories WPF 判断USB插拔 lindexi 2018-08-10 19:16:53 +0800 2018-8-5 13:0: ...

  3. 通过解读 WPF 触摸源码,分析 WPF 插拔设备触摸失效的问题(问题篇)

    在 .NET Framework 4.7 以前,WPF 程序的触摸处理是基于操作系统组件但又自成一套的,这其实也为其各种各样的触摸失效问题埋下了伏笔.再加上它出现得比较早,触摸失效问题也变得更加难以解 ...

  4. WPF 插拔触摸设备触摸失效

    原文:WPF 插拔触摸设备触摸失效 最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效.通过分析 WPF 源代码可以找到 WPF 触摸失效的原因. 在 Windows 会将所有的 H ...

  5. QTC++监控USB插拔

    #if defined(Q_OS_WIN) #include <qt_windows.h> #include <QtCore/qglobal.h> #include <d ...

  6. USB线插拔检测使用UEventObserver检测uevent事件的分析

    说实话这玩样儿的代码量真的很少,大家如果能耐得住性子啃一会儿也就能撸懂了. 在这之前研究USB线插拔的时候就知道了有这么个东西,当时也就看了看,但没做什么笔记.最近想用起来,却发现就只有个名字在记忆中 ...

  7. android6.0 外部存储设备插拔广播以及获取路径(U盘)【转】

    本文转载自:https://blog.csdn.net/zhouchengxi/article/details/53982222 这里我将U盘作为例子来说明解析. android4.1版本时U盘插拔时 ...

  8. [未完] Linux 4.4 USB —— spiflash模拟usb大容量存储设备 调试记录 Gadget Mass Stroage

    linux 4.4 USB Gadget Mass Stroage 硬件平台: licheepi nano衍生 调试记录 驱动信息 │ This driver is a replacement for ...

  9. C#.NET U盘插拔监控

    [1]涉及的知识点 1) windows消息处理函数 ? 1 protected override void WndProc(ref Message m) 捕获Message的系统硬件改变发出的系统消 ...

  10. ARM上的linux如何实现无线网卡的冷插拔和热插拔

    ARM上的linux如何实现无线网卡的冷插拔和热插拔 fulinux 凌云实验室 1. 冷插拔 如果在系统上电之前就将RT2070/RT3070芯片的无线网卡(以下简称wlan)插上,即冷插拔.我们通 ...

随机推荐

  1. 对象命名为何需要避免'-er'和'-or'后缀

    之前写过两篇关于软件工程中对象命名的文章:开发中对象命名的一点思考与对象命名怎么上手?从现实世界,但感觉还是没有说透, 在软件工程中,如果问我什么最重要,我的答案是对象命名.良好的命名能够反映系统的本 ...

  2. MySQL中怎么分析性能?

    MySQL中主要有4种方式可以分析数据库性能,分别是慢查询日志,profile,Com_xxx和explain. 慢查询日志 先用下面命令查询慢查询日志是否开启, show variables lik ...

  3. 关于SIFT,GIFT在旋转不变性上的对比实验

    目录 关于SIFT,GIFT在旋转不变性上的对比实验 回顾 准确率测试 总结 核心代码 关于SIFT,GIFT在旋转不变性上的对比实验 这篇文章不讨论SIFT,GIFT的实现原理,只从最终匹配结果的准 ...

  4. ANSYS 启动窗口过大问题解决

    方法总结(省流版):选择兼容性下更改高 DPI 设置 => 勾选高DPI 缩放代替 ,且其下对应应用程序选项 1.环境 系统环境:Windows 11 设备情况:分辨率 1920×1080:缩放 ...

  5. 【Java】NIO

    1. Java NIO 简介 Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API. NIO与原来的IO有同样的作用和目的,但是 ...

  6. study PostgreSQL【1-PostgreSQL对象】

    1.服务 PostgreSQL是作为一种服务安装在操作系统下.多个PostgreSQL服务可以运行于同一台问你服务器上,但是他们侦听端口不能重复,也不能共享同一个数据存储目录. 2.Database ...

  7. unigui的程序编译后自动运行傻傻的手动【7】

    我们每次修改unigui程序后,一般需要编译后执行,查看效果.可是每次都要关闭杀掉服务程序,再刷新浏览器才能实现. EMB应该知道这个反人类的做法吧.实际上提供了参数配置:自动kill服务程序,自动打 ...

  8. lua三色标记的读写屏障理解

    起因是已经被标记为黑色的对象无法进行再次遍历,然而黑色对象发生了引用变化:断开了引用或者引用了别的对象,会导致多标(不再被黑色对象引用的对象未能回收),漏标(黑色对象的新引用未能遍历标记)

  9. Mono GC

    1.虽然是stw但mark阶段可以concurrent 2.并行mark就需要写屏障 3.unity的gc也不是扫描整个堆内存 https://schani.wordpress.com/2012/12 ...

  10. 使用open-feign进行远程服务调用

    想要远程调用别的服务 1).引入open-feign包 2).编写一个接口,告诉SpringCloud这个接口是调用哪个远程的服务 a.声明接口的每一个方法都是调用哪个远程服务的那个请求 3).开启远 ...