技术背景

我们的板子作为 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. Tomcat之——宕机自动重启和每日定时启动tomcat

    在项目后期维护中会遇到这样的情况,tomcat在内存溢出的时候就出现死机的情况和遇到长时间不响应,需要人工手动关闭和重启服务,针对这样的突发情况,希望程序能自动处理问题而不需要人工关于,所以才有了目前 ...

  2. Linux netstat 命令查看80端口状态

    Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Membershi ...

  3. PLSQL自动登录,记住用户名密码&日常使用技巧

    配置启动时的登录用户名和密码 这是个有争议的功能,因为记住密码会给带来数据安全的问题. 但假如是开发用的库,密码甚至可以和用户名相同,每次输入密码实在没什么意义,可以考虑让PLSQL Develope ...

  4. StringBuilder案例

    1.案例一 如图 这里无法使用反转方法的原因是,s属于String类型,而反转的方法存在于StringBuilder类型,所以我们要将s的类型转换为StringBuilder String--> ...

  5. 阿里云平台OSS对象存储

    OSS即"OpenStorageService",概念上没啥新意,就是本地存储搬到阿里云平台上了,单个存储对象大小可以达到5G,看了下阿里的OSS教程java版本, 使用原生js和 ...

  6. 《机器人SLAM导航核心技术与实战》第1季:第7章_SLAM中的数学基础

    <机器人SLAM导航核心技术与实战>第1季:第7章_SLAM中的数学基础 视频讲解 [第1季]7.第7章_SLAM中的数学基础-视频讲解 [第1季]7.1.第7章_SLAM中的数学基础_S ...

  7. 洛谷P4198 楼房重建 题解

    Part1.自己一开始是怎么想的 我一开始的想法是先考虑什么情况下是看不见的. 如果是 \(i < j\) 的话可以直接看 \(j\) 的斜率和 \(i\) 的斜率就是比较 \(\frac{h_ ...

  8. CH9121default与classical设置方法

    SYN发送间隔调整方法: 网口连接设备后双击设备列表中要配置的设备在扩展参数中单击获取扩展参数,在超时处理模式选项选择Classical然后执行设置扩展参数,最后点击复位模块后生效(仅TCP CLIE ...

  9. C#之集合常用扩展方法与Linq

    一.集合的常用扩展方法(lambda的方式) 1.Where() 根据条件选择数据 2.Select() 根据数据条件转换成新的数据类型,类似于DTO转换类 3.Max() 根据条件选择最大值 4.M ...

  10. Java---switch...case中case可以匹配些什么

    switch-case语句 case 标签可以是 : •类型为 char.byte.short 或 int 的常量表达式. •枚举常量. •从 Java SE 7 开始,case 标签还可以是字符串字 ...