USB Gadget设备枚举失败的处理方法
技术背景
我们的板子作为 USB Gadget 设备通过 USB 线接入 USB 主机使用,我们的板子被主机识别为一个 Compsite Device,这个 Compsite Device 是由我们板子根据 Host 口实际接的 USB 设备动态创建的,所以它包含哪些功能,由接在 Host 口的设备决定。假设我们的板的 USB Host 口接了一个键盘和一个鼠标,那个我们的板就会被主机识别为一个支持键盘和鼠标功能的 Compsite Device。我们板上 Host 口接的设备的数据会被转发给 USB 主机。
问题描述
某个鼠标接入我们板 USB Host 口后,我们板子使用此鼠标创建的 USB Gadget 设备无法被 Windows 系统枚举成功,异常现象为:
- 一个联想鼠标,接入我们板,我们板作 USB Gadget 接入 WIN10 系统后,系统无法识别,无法完成 USB 枚举过程
- 这个联想鼠标直接接在 WIN10 系统上,是能正常识别和工作的
- 这个联想鼠标接入 Ubuntu 系统,也是能正常识别和工作的
分析过程
从应用层程序入手,排除 USB 设备描述符与 HID 报文描述符设置等应用层问题,对比分析 Linux 与 Windows 系统 USB 枚举过程差异,跟踪 Linux 内核代码 configfs 驱动部分。
原因解析
Linux 系统在 USB 设枚举过程中获取到设备的实际 HID 报文描述符长度后,会使用此实际长度作为期望的长度,来获取 HID 报文描述符。而 WIN10 会使用实际长度另加上 64 作为期望长度来获取 HID 报文描述符。这是 Windows 系统和 Linux 系统的差异,这个差异会引起异常,而 WIN10 的这种处理机制也是符合 USB 规范的,所以此异常应该是 Linux 内核的一个 BUG。
主机端在获取 HID 报文描述符时如何判断设备端已经应答完毕是这个问题的关键。
控制传输中的最大包长:高速设备最大包长是 64 字节;低速设备是 8;全速设备可以是 8 或 16 或 32 或 64。最大包长表示一个端点单次接收/发送数据的能力,实际上就是该端点对应的缓冲区的大小。当一次传输的数据量超过该端点的最大包长时,需要将数据拆分成多个包传输,只有最后一个包可以小于最大包长,除最后一个包外的其他包都应等于最大包长。所以在一次控制传输中,如果一个端点收到/发送了一个长度小于最大包长的包,则表示此次数据传输结束。
在设备枚举阶段,传输类型是控制传输,最大包长由 MaxPacketSize 参数(对于 USB 2.0 设备此值是 64)指定。一次控制传输的数据会被切割成多个包,只有最后一个包的长度可以小于最大包长。当主机接收到的数据量已经等于期待数据长度时,直接就可以判定应答已经结束了。当主机接收的数据量小于期待数据长度时,若接收到的最新的应答数据包的长度小于最大包长,说明这个包是这次传输的最后一个包,表示应答已完毕;若最新收到的数据包等于最大包长,主机是无法判断这个包是不是本次传输的最后一个包的 ,USB 协议规定,针对这种情况,当设备端应答的数据量小于主机侧期待的数据长度,而应答数据量又刚好能被最大包长整除(即被切割成多个完整的包),就需要额外再发送一个 ZLP 包(Zero Length Packet)以指示本次传输完成。
异常的这个联想鼠标,它的 HID 报文描述符是 64 字节长(刚好等于 MaxPacketSize 参数),当 WIN10 系统以期望长度 128(64+64) 来获取 HID 报文描述符时,内核 Gadget 驱动只回复了实际的 64 个字节(它没有发 ZLP 包)的 HID 报文描述符,此时 Linux Gadget 认为自己已经应答完毕,而 WIN10 收到的回复不足 128 字节但最后一个包又是整包,它认为传输还没完成一直在等新数据,直到超时,总线进入异常状态,WIN10 复位 USB 总线,设备侧内核 USB 驱动模块进入错乱状态。原因就是,Linux Gadget 驱动在这种特殊情况下没发 ZLP 包。这就是问题描述中现象 1 的原因。
为什么这个鼠标接在 Ububtu 上又正常呢,因为 Ubuntu 期待的长度就是 64,收到 64 字节的应答,它就认为应答已经完毕。
解决方法
知道了原因就好解决了。修复内核 BUG,在 Gadget 的驱动代码里,判断当一次应答的数据量小于主机期待的长度且应答数据量又能被最大包长整除时,多发一个 ZLP 包。问题解决。
USB Gadget设备枚举失败的处理方法的更多相关文章
- 浅析USB之设备枚举
当一个USB设备插入主机后,会有以下活动: 配上状态图
- Windows与自定义USB HID设备通信说明.
1 . 所使用的典型 Windows API CreateFile ReadFile WriteFile 以下函数是 DDK 的内容: HidD_SetFeature HidD_GetFeatur ...
- C#:USB设备枚举 --转自CSDN作者:Splash
(一)DeviceIoControl的PInvoke /* ---------------------------------------------------------- 文件名称:Device ...
- 控制器没有足够的带宽可利用为USB大容量存储设备的解决方法
伴随网盘时代的没落,最近刚入手了一个移动硬盘.现在的移动硬盘都是USB3.0,传输速度比USB2.0要快很多.但是链接笔记本电脑后发现传输速度在20MB/s左右,跟USB2.0速度差不多,并不能达到传 ...
- 移动设备 小米2S不显示CD驱动器(H),便携设备,MTP,驱动USB Driver,MI2感叹号的解决方法
小米2S不显示CD驱动器(H),便携设备,MTP,驱动USB Driver,MI2感叹号的解决方法 by:授客 QQ:1033553122 用户环境 操作系统:Win7 手机设备:小米2S 问题描 ...
- USB设备驱动之设备初始化(设备枚举)
USB设备从接入HUB到正常工作之前.都属于设备枚举阶段.所谓设备枚举.就是让host控制器认识USB设备,并为其准备资源.建立好主机与设备间的数据传递机制. 该阶段的工作,是USB通信协议规定的,所 ...
- 弹出USB大容量存储设备时出问题的解决方法
我的计算机->管理->系统工具->事件查看器->自定义视图->Kernel-Pnp->详情->进程ID 然后在任务管理器里找到该进程(任务管理器->查看 ...
- [未完] Linux 4.4 USB —— spiflash模拟usb大容量存储设备 调试记录 Gadget Mass Stroage
linux 4.4 USB Gadget Mass Stroage 硬件平台: licheepi nano衍生 调试记录 驱动信息 │ This driver is a replacement for ...
- USB gadget 驱动 printer.c 分析
1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模块参数 2. prn_example从stdout获取数据然 ...
- Android USB gadget configfs学习笔记总结
1.一个config_item 是通过显式用户空间mkdir操作创建的,通过rmdir销毁.属性(文件)在mkdir之后出现,可以通过read和write读取或修改属性文件.与sysfs一样,read ...
随机推荐
- 使用Avalonia/C#构建一个简易的跨平台MCP客户端
前言 前几天介绍了在C#中构建一个MCP客户端. 最近正在学习Avalonia,所以就想用Avalonia实现一个简易的跨平台MCP客户端.接入别人写的或者自己写的MCP服务器就可以利用AI做很多有意 ...
- K8S基本概念和组件
特点 便携性 无论公有云.私有云.混合云还是多云架构都全面支持 可扩展 模块化.可插拔.可挂载.可组合,支持各种形式的扩展 自修复 自保持应用状态.自重启.自复制.自缩放,声明式语法 组件 etcd ...
- HTML中,table怎样使用
<table> 标签用于在HTML中创建表格.表格是一种以行和列的形式组织和显示数据的结构化方式.<table> 标签通常与其他相关标签(如 <tr>.<th ...
- study PostgreSQL【2-FireDAC连接PostgreSQL】
就这么个简单问题,一下午时间.想想就憋屈. 那么牛逼哄哄FireDAC居然连接PostgreSQL出问题了.帮助中说的啥意思,咱也不明白.网上一通也是云里雾里. 上干货,具体点: TFDConnect ...
- 探秘Transformer系列之(21)--- MoE
探秘Transformer系列之(21)--- MoE 目录 探秘Transformer系列之(21)--- MoE 0x00 概要 0x01 前置知识 1.1 MoE出现的原因 1.1.1 神经网络 ...
- 线性探测法的查找函数 作者 DS课程组 单位 浙江大学
虽然但是,我真的讨厌c语言这样一大坨typedef命名来命名去的,很多时候其实我们会写,但是看不懂这个存储结构 函数的接口定义 Position Find( HashTable H, ElementT ...
- Fast Prefix Sum Implementation Using Subgroups in GLSL Compute Shaders
利用 Vulkan 1.1 的 subgroup 特性加速 ComputeShader 的前缀和计算,参考: Vulkan Subgroup Tutorial - Khronos Blog - The ...
- markdown常用命令行格式
Markdown 主要命令(语法)如下: 标题 使用 # 号表示标题,# 的个数决定标题的级别: 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 段落 & 换行 直接输入文字形成段 ...
- Assets, Resources and AssetBundles(五):AssetBundle usage patterns
这是系列文章中的第五章,内容涉及"Unity5"中的资产.资源和资源管理. 本系列的前一章介绍了AssetBundles的基本原理,其中包括各种加载API的低级行为.本章讨论了在实 ...
- 深入理解Java虚拟机-JAVA内存模型与线程
Java内存模型(JMM) JMM 的核心概念 主内存与工作内存: 主内存(Main Memory)是所有线程共享的内存区域,存放着所有变量的值 每个线程都有自己的 工作内存(Working Memo ...