技术背景

我们的板子作为 USB Gadget 设备通过 USB 线接入 USB 主机使用,我们的板子被主机识别为一个 Compsite Device,这个 Compsite Device 是由我们板子根据 Host 口实际接的 USB 设备动态创建的,所以它包含哪些功能,由接在 Host 口的设备决定。假设我们的板的 USB Host 口接了一个键盘和一个鼠标,那个我们的板就会被主机识别为一个支持键盘和鼠标功能的 Compsite Device。我们板上 Host 口接的设备的数据会被转发给 USB 主机。

问题描述

某个鼠标接入我们板 USB Host 口后,我们板子使用此鼠标创建的 USB Gadget 设备无法被 Windows 系统枚举成功,异常现象为:

  1. 一个联想鼠标,接入我们板,我们板作 USB Gadget 接入 WIN10 系统后,系统无法识别,无法完成 USB 枚举过程
  2. 这个联想鼠标直接接在 WIN10 系统上,是能正常识别和工作的
  3. 这个联想鼠标接入 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设备枚举失败的处理方法的更多相关文章

  1. 浅析USB之设备枚举

    当一个USB设备插入主机后,会有以下活动: 配上状态图

  2. Windows与自定义USB HID设备通信说明.

    1 .   所使用的典型 Windows API CreateFile ReadFile WriteFile 以下函数是 DDK 的内容: HidD_SetFeature HidD_GetFeatur ...

  3. C#:USB设备枚举 --转自CSDN作者:Splash

    (一)DeviceIoControl的PInvoke /* ---------------------------------------------------------- 文件名称:Device ...

  4. 控制器没有足够的带宽可利用为USB大容量存储设备的解决方法

    伴随网盘时代的没落,最近刚入手了一个移动硬盘.现在的移动硬盘都是USB3.0,传输速度比USB2.0要快很多.但是链接笔记本电脑后发现传输速度在20MB/s左右,跟USB2.0速度差不多,并不能达到传 ...

  5. 移动设备 小米2S不显示CD驱动器(H),便携设备,MTP,驱动USB Driver,MI2感叹号的解决方法

    小米2S不显示CD驱动器(H),便携设备,MTP,驱动USB Driver,MI2感叹号的解决方法 by:授客 QQ:1033553122 用户环境 操作系统:Win7 手机设备:小米2S   问题描 ...

  6. USB设备驱动之设备初始化(设备枚举)

    USB设备从接入HUB到正常工作之前.都属于设备枚举阶段.所谓设备枚举.就是让host控制器认识USB设备,并为其准备资源.建立好主机与设备间的数据传递机制. 该阶段的工作,是USB通信协议规定的,所 ...

  7. 弹出USB大容量存储设备时出问题的解决方法

    我的计算机->管理->系统工具->事件查看器->自定义视图->Kernel-Pnp->详情->进程ID 然后在任务管理器里找到该进程(任务管理器->查看 ...

  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. USB gadget 驱动 printer.c 分析

    1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模块参数 2. prn_example从stdout获取数据然 ...

  10. Android USB gadget configfs学习笔记总结

    1.一个config_item 是通过显式用户空间mkdir操作创建的,通过rmdir销毁.属性(文件)在mkdir之后出现,可以通过read和write读取或修改属性文件.与sysfs一样,read ...

随机推荐

  1. oracle服务 linux启动命令

    一.Linux下启动Oracle Linux下启动Oracle分为两步: 1)启动监听: 2)启动数据库实例: 1.登录服务器,切换到oracle用户,或者以oracle用户登录 [admin@dat ...

  2. el-tree 动态图标

    举个栗子 https://jsfiddle.net/taadis/x9crjsum/ https://jsrun.net/MYXKp - 上面看不了的,可以看这个...

  3. Codeforces Round 1016 (Div. 3)题解

    题目地址 https://codeforces.com/contest/2093 锐评 在所有题意都理解正确的情况下,整体难度不算太难.但是偏偏存在F这么恶心的题意,样例都不带解释一下的,根本看不懂题 ...

  4. Web前端入门第 31 问:CSS background 元素背景图用法全解

    background 可设置背景色.渐变.背景图等,本文主要讲解背景图片的用法. 背景顾名思义就是背后的景色,始终居于元素背后,元素永远站在背景的身前. 本文示例中所使用的图片: background ...

  5. RocketMQ的Producer是如何发送消息的

    RocketMQ 的 Producer 发送消息过程涉及多个步骤,包括初始化.消息创建.发送方式选择 1.Producer初始化 首先,我们需要创建并初始化一个Producer示例 这段代码完成了以下 ...

  6. 🎀SpringBoot启动创建系统托盘及功能

    简介 SpringBoot启动时,创建系统托盘,提供打开主程序及退出功能. 实现 启动类添加构造函数 public TjtoolApplication() { initUI(); } private ...

  7. 查看MySQL是否安装成功

    1)安装了Windows Service:MySQL80,并且已经启动. 2)安装了MySQL软件.安装位置为:C:\Program Files\MySQL  (默认路径) (MySQL文件下放的是软 ...

  8. Java 的 CMS 垃圾回收器和 G1 垃圾回收器在记忆集的维护上有什么不同?

    Java 的 CMS 垃圾回收器和 G1 垃圾回收器在记忆集的维护上的不同 记忆集(Remembered Set, RSet)是垃圾回收器用来跟踪跨代引用的重要结构,它记录老年代对象对新生代对象的引用 ...

  9. 使用xxxbase应付CRUD后端任务

    很多的后端CRUD开发任务都是毫无意义的, 如果使用firebase/supabase/pocketbase这些工具快速应付这些任务才是重要的. 如果是一位研究生, 在面对导师的垃圾横向的时候, 这种 ...

  10. Vue3+Ant-design项目启用ts/typescript

    Ant-design官方文档提供了js和ts两种案例,按照文档给项目install ant-design后写了个组件编译时发现只要加上`<script lang="ts"&g ...