技术背景

我们的板子作为 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. 【前端开发】记一次Echart 内存泄露问题的排查

    最近发现一个web项目总是莫名其妙的内存增长,然后进行定位后来发现问题大概率出在Eharts上. 于是乎就开始搜索关于echarts内存增长的一些例子,但是都没有结果. 其中翻博客时发现甚至有人换成一 ...

  2. PLSQL Developer使用大全

    第一章 PLSQL Developer特性 PL/SQL Developer是一个集成开发环境,专门面向Oracle数据库存储程序单元的开发.如今,有越来越多的商业逻辑和应用逻辑转向了Oracle S ...

  3. Linux下查询tomcat进程命令

    由于查询tomcat进程时将ps -ef|grep tomcat命令记错为ps -f|grep tomcat命令,因此对比两个命令进行区分. ps -f |grep tomcat执行结果: dgztc ...

  4. ORACLE物理文件存储位置查询语句

    在进行备份和恢复的时候,我们需要知道一些关于ORACLE物理文件存储的信息,这样我们才能判断我们所进行的备份是否完整.一个完整的备份需要包括的物理文件:控制文件联机重做日志文件归档日志文件数据文件因此 ...

  5. post数据到第三方,中文乱码

    1.项目中发现 测试环境 推送正文,数据正常 2.生产到腾讯云之后,中文推送过去乱码,但是post 接口的 时候,指定了 编码格式. 3.后查看日志,发现日志中记录的中文就是乱码 4.排查cs代码文件 ...

  6. 学习unigui【21】unistringGrid的标题栏动态增加

    var Column: TUniGridColumn; begin Column := TUniGridColumn(unstrngrd_summary.Columns.Add); Column.Ti ...

  7. yolov5常用命令记录

    一.准备深度学习环境 首先,确保你的计算机上已经安装了Python.PyTorch以及CUDA等必要的深度学习框架和库.YOLOv5对Python版本和PyTorch版本有一定的要求,通常建议使用Py ...

  8. 一文速通Python并行计算:08 Python多进程编程-multiprocessing模块、进程的创建命名、获取进程ID、创建守护进程和进程的终止

    一文速通 Python 并行计算:08 Python 多进程编程-multiprocessing 模块.进程的创建命名.获取进程 ID.创建守护进程和进程的终止 摘要: 本节介绍 Python 中 m ...

  9. 最详细最易懂的【YOLOX原理篇】

    目录 前言 简介 详细解读 Mosaic and Mixup Mixup Mosaic Decoupled Head anchor free SimOTA in_boxes 和 in_center 计 ...

  10. java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap报错解决办法

    在接收表单数据封装成对象时报错,具体错误信息如下: java.lang.reflect.InvocationTargetException     at sun.reflect.NativeMetho ...