简介:面向硬件开发者、嵌入式工程师与USB协议学习者的中文版USB3.0协议规范文档,针对英文原版阅读门槛高、术语理解困难的问题,提供可直接查阅的译制资料,适合产品开发、驱动调试与协议入门等场景。压缩包内共1个PDF文件,约5.61MB,内容完整覆盖超速结构分层、协议层端到端通讯规则、突发传输与流控、ITP时间戳同步、链路电源管理、HUB与主机双总线结构、管道与流式传输模型等核心章节,并附USB3.0与USB2.0在事务构成、双工方式、轮询机制、流能力及低功耗策略上的逐项对比。已有3509人学习,读者可据此系统梳理超速协议框架,理解端点描述符、NRDY/ERDY握手与电源状态切换等机制,为设备开发与问题排查提供可对照的规范依据。
1. 超速总线的反直觉设计:为什么 USB3.0 不再“轮询”了
USB2.0 时代,主机像个焦虑的监工,每隔 125 微秒就要挨个问设备“你有数据吗?你能收吗?”,哪怕设备正在发呆,这次问询也照跑不误。这种轮询机制在低速外设上勉强能忍,但当你要拷一个 20GB 的虚拟机镜像时,总线上一半的时间都浪费在“你准备好了吗”这种无效对话上。USB3.0 的超速结构做了一个反直觉的翻转:把“问”的权力从主机手里拿走一部分,交给设备。设备没准备好就回一个 NRDY,等它缓过劲来,主动发 ERDY 通知主机“我现在行了”。主机不再瞎问,总线不再空转。这个机制背后是一整套分层通讯结构、双总线兼容和链路级电源管理的重新设计,搞懂它,你才能明白为什么同一根线缆既能跑 5Gbps 又能向下兼容,也才能在调试枚举失败、突发传输掉速时知道该往哪个方向查。这份中文版协议规范把协议层、链路层、包结构和传输类型拆得足够细,正好用来把“为什么能 work”这件事从寄存器层面讲清楚。
2. 协议层与管道模型:主机导向的通讯规则到底怎么落地
2.1 端到端通讯与管道:谁在决定数据什么时候走
超速协议在主机和设备端点之间定义了一条逻辑连接,叫管道(pipe)。管道不是物理线缆,而是一组缓存能力和传输规则的抽象。主机软件往管道里写数据,设备端点从管道里读;反过来设备端点往管道里写,主机软件从管道里读。关键在于,这个通讯关系是主机导向的——主机决定什么时候发起应用数据传输,设备不能强行往总线上灌数据。但设备保留了一个后门:它可以通过特定端点向主机发起异步请求服务,这就是 ERDY 通知的协议基础。
常见的实现思路是,主机控制器维护一张管道表,每个表项记录端点地址(设备地址、端点号、方向)、传输类型、最大包尺寸、突发大小和当前状态。当主机软件提交一个传输请求时,控制器查表找到对应管道,按传输类型生成事务包。设备侧则在端点缓存满或空时触发流控事件,发 NRDY 让主机把该管道从活动调度队列里摘出去,等缓存状态变化再发 ERDY 重新入队。这个“摘出-重入”的动作是超速总线效率提升的核心,因为它让不活跃的管道彻底不占用总线时间。
注意:ERDY 是设备主动发的,不是主机轮询出来的。调试时如果看到设备一直不发 ERDY,先查端点缓存是不是一直处于“满”或“空”的僵持状态,而不是去翻主机调度器。
2.2 双总线结构:HUB 为什么逻辑上是两个 HUB 的叠加
USB3.0 要向下兼容 USB2.0,这件事在 HUB 上体现得最赤裸。一个 USB3.0 HUB 在逻辑上是两个独立 HUB 的组合:一个 USB2.0 HUB 和一个 USB3.0 HUB,共享上游端口的电源和地线,但数据路径完全分开。超速部分走那对额外的差分线,USB2.0 部分走原来的 D+/D-。主机侧也一样,主控器必须同时包含超速和 USB2.0 两部分,各自管理自己总线上的控制、状态和信息交换。
这种设计的动机很实际:如果强行让 USB3.0 HUB 在协议层兼容 USB2.0 的轮询事务,那超速的异步通知和突发传输就没法干净地实现。拆成两个逻辑 HUB,超速 HUB 只负责路由超速包、广播 ITP、处理低功耗端口唤醒;USB2.0 HUB 继续跑它的轮询老路。设备插上来时,主机先通过 USB2.0 总线做初始枚举,如果发现设备支持超速,再切换到超速总线重新枚举。你看到设备管理器里一个设备出现两次枚举记录,根子就在这。
集线器在超速端到端协议里承担的具体工作包括:路由输出包到下游端口、把输入包混合传递到上游端口、在非低功耗状态下向所有下游端口广播时间戳包(ITP)、检测到低功耗端口有包时把目标端口转出低功耗并通知主机和设备。这些动作里,路由和广播是硬件逻辑,唤醒通知则涉及带内信令,调试链路训练失败时经常卡在唤醒握手这一步。
2.3 数据流模型:流式管道与消息管道的分工
超速继承了 USB2.0 的数据流模型,管道分两类:流式管道和消息管道。流式管道跑数据,没有 USB2.0 定义的结构,就是裸的数据流;消息管道跑控制,有指定的请求结构。大多数管道在系统软件配置设备后才存在,但设备上电进入默认状态后,默认控制管道(端点 0)总是存在的,用来访问设备描述符、配置状态和控制信息。
每个管道支持 USB2.0 定义的四种传输类型之一,管道属性和端点属性一致。超速对海量传输(bulk)做了扩展,叫流(stream)。流式提供在协议级支持在标准块传输管道中多路传输多个独立的逻辑数据流。这个能力的实际意义是:一个 bulk 端点可以同时承载多个应用的数据流,每个流有独立的流 ID,主机和设备通过流 ID 区分。常见的实现思路是在 DPH 里划出流 ID 字段,端点侧维护多组顺序号和缓存队列。没有这个机制,多线程应用共享一个 bulk 端点时只能靠应用层自己加锁分帧,效率低且容易出错。
端点属性记录在描述符和 SuperSpeed Endpoint Companion Descriptor 里,包括最大包尺寸、突发大小等。端点用三个参数寻址:设备地址、端点号、方向。所有超速设备必须至少在默认控制管道上开始执行,这是枚举的起点,没有例外。
3. 事务处理的重构:突发、异步通知与全双工怎么配合
3.1 从三段式到分离事务:令牌包去哪了
USB2.0 的一个事务分三段:令牌、数据、握手。主机先发令牌包告诉总线上所有设备“我要找地址 X 端点 Y 方向 Z”,然后发数据,最后等握手。超速还是这三段,但用法变了:令牌包集成在头包和 DPH 里,各种握手包都以 TP 包形式存在。对于 OUT 事务,令牌被合并在数据包中,设备地址和端点信息直接在 DPH 里;对于 IN 事务,令牌被握手包代替,主机发一个 ACK TP 给设备,里面带地址信息,设备回数据或者 NRDY/STALL。
这个重构的底层原因是全双工。USB2.0 是半双工广播总线,同一时刻只能一个方向传,所以必须用独立的令牌段来仲裁。超速是 dual-simplex 非广播总线,发送和接收路径独立,可以同时进行 IN 和 OUT 事务。令牌信息塞进包头后,主机发 ACK TP 请求 IN 数据的同时,设备可以已经在发上一个 OUT 事务的握手了。协议上允许在收到握手包之前发送多个数据包,这就是突发。
用一段伪代码描述主机侧 IN 事务的调度逻辑,能看清顺序号的作用:
# 主机侧 IN 事务调度(简化示意) def schedule_in_transfer(endpoint, max_burst): seq = 0 # 期望的设备发送顺序号,初始为 0 while not transfer_done: # 发 ACK TP,携带端点地址、期望顺序号、允许的突发包数 send_ack_tp(endpoint.addr, endpoint.ep_num, seq, max_burst) # 不等待每个包,设备可以连续发最多 max_burst 个 DP for i in range(max_burst): dp = receive_data_packet(timeout) if dp is None: break if dp.seq != (seq + i) % 32: # 顺序号不匹配,触发重传,从期望号重新请求 handle_sequence_error(dp.seq) break deliver_to_host_buffer(dp.payload) seq = (seq + received_count) % 32 # 设备回 NRDY 则把端点移出活动队列,等 ERDY 再恢复 if last_handshake == NRDY: remove_from_active_schedule(endpoint) wait_for_erdy(endpoint)这段逻辑里,seq是 5 位顺序号,范围 0 到 31,循环使用。主机发 ACK TP 时带上期望顺序号,设备按序发数据包,每个 DP 的 DPH 里带自己的顺序号。主机收到顺序号不对的包,说明中间丢了,从期望号重新请求。设备不需要等每个包的 ACK 就能继续发,因为全双工路径上 ACK TP 和数据包可以同时流动。这就是突发消除等待延迟的机制。
3.2 突发传输:最大突发大小从哪来、怎么用
突发数据通过消除每个数据包应答的等待时间提高效率。每个超速设备端点指示它在必须等待一个明显握手之前能发送或接受的包数量,叫最大数据突发大小。这个值从 SuperSpeed Endpoint Companion Descriptor 里读,主机在配置阶段拿到。
主机在每个事务处理基础上能动态改变突发大小,直到最大突发大小被配置。对于 OUT 端点,主机容易控制突发大小,因为接收器必须总能管理突发大小事务;对于 IN 端点,主机通过在发给设备的 ACK TP 里的一个域来限制端点突发大小。这个动态调整的实际用途是公平调度:当总线上挂了很多 bulk 端点,主机可以每次事务用较小的突发,把带宽均摊给所有挂起的流;当只有一个端点活跃,就用满最大突发,跑满带宽。
bulk 端点的最大包大小是 1024 字节,突发大小范围 1 到 16。control 端点最大包大小 512 字节,不支持突发,最大突发大小为 1。interrupt 和 isochronous 端点可以突发 1 到 16,但当最大突发大小大于 1 时,最大包大小只能是 1024;等于 1 时,isochronous 最大包大小可以是 0 到 1024 任意值,interrupt 是 1 到 1024 任意值。这些数字不是随便定的,1024 字节对应端点缓存的典型 SRAM 页大小,16 的突发上限对应链路层缓冲区深度,再大就会挤占其他端点的缓存。
3.3 NRDY 与 ERDY:流控事件怎么改变调度队列
超速的非同步端点忙时会返回 NRDY 应答,当它想又要服务时发送 ERDY 通知。主机在下一个传输类型限制下的有效时机中重新安排事务。这个机制替代了 USB2.0 的 NAK 握手——USB2.0 里设备回 NAK,主机下一轮轮询再来问,总线时间被反复消耗;超速里设备回 NRDY,主机直接把该端点从活动调度队列移除,等 ERDY 来了再插回去。
设备告知(Device Notification)是另一个层面的异步通信:设备向主机进行设备级和总线级的事件通知,不映射到标准传输类型的管道模式,总是由设备开始,数据流从设备到主机。常见的实现思路是用一个专门的端点或者链路级消息通道承载通知,主机侧注册回调。这个机制让设备能在不占用数据管道的情况下报告状态变化,比如连接状态、功能就绪等。
注意:NRDY 和 STALL 是两回事。NRDY 是“暂时不行,等我通知”,STALL 是“端点停了,需要控制传输清除”。调试时把 NRDY 当 STALL 处理,会导致主机反复发 ClearFeature,越清越乱。
4. 包结构与链路层:16 字节头包和 CRC 保护怎么撑起可靠性
4.1 四种基本包类型:LMP、TP、DP、ITP 的分工
超速包以 16 字节的头部开始,一些包只包含头部,比如 TP、LMP、ITP。所有头部以包类型信息开始,决定包处理方式。头部有 16 位 CRC 保护,以 2 字节链路控制字结束。依赖类型,大多数包包含路由信息和一个三参数设备地址(设备地址、端点号、方向)。路由字符给主机用来指导包被发送到被指向的拓扑路径;设备发送的包被集线器默认路由选择,集线器总是把数据从任何可见的下游端口传到上游端口,这一过程不需要路由信息。
四种基本类型的分工很清晰:LMP 只穿过一对直接连接的端口,管理链路本身,比如链路训练、电源状态切换;TP 穿过所有直接连接主机与设备的链路,用来控制流式数据包、配置设备和集线器,任何传输类型的事务处理都用到,注意 TP 没有数据,就是一个包头;DP 穿过所有直接连接主机与设备的链路,由 DPH 和带 32 位 CRC 的数据负载包 DPP 组成;ITP 由主机多点广播到所有活动链路,携带时间戳。
这个分层的关键在于,LMP 是链路本地的,不参与端到端路由,所以链路训练失败和端点枚举失败是两类完全不同的故障。前者查物理层和 LMP 交互,后者查 TP 和描述符。
4.2 头包帧与 CRC:20 符号长度里的容错设计
所有头包有 20 符号长度,由头包帧、包头、链路控制字三部分组成。头包帧是 HPSTART 顺序集,4 个基于 K 标志的有序集合,定义为 3 个连续的 SHP 标志后面跟着一个 K-symbol 的 EPF。头包总是以 HPSTART 序列集开始,这个结构能容忍 1 个符号错误。
包头由 14 个字节组成:12 字节头信息加 2 字节 CRC-16。CRC-16 保护 12 字节头信息的完整性。数据包的 DPP 区域有 0 到 1024 字节数据,后面跟 4 字节 CRC-32。DPP 帧由 8 个 K-symbols 构成,起始帧 DPPSTART 是三个连续 SDP 加一个 EPF,结束帧有两种:DPPEND 是三个连续 END 加 EPF,表示正常结束;DPPABORT 是三个连续 EDB 加 EPF,表示半途终止。DPP 应该快速无缝地跟着对应的 DPH,中间不应该有空间间隙。
这套帧结构的容错设计值得细看。HPSTART 容忍 1 个符号错误,意味着链路层在符号边界抖动时还能正确识别包头起始;CRC-16 保护头信息,CRC-32 保护数据负载,两级 CRC 分开是因为头包错误和负载错误的恢复策略不同——头包错了整包重传,负载错了可以只重传数据部分。DPPABORT 的存在是为了处理传输中途终止的情况,比如设备缓存溢出或者主机取消传输,接收方看到 DPPABORT 就知道这个 DPP 不完整,丢弃而不是等 CRC 校验失败。
4.3 链路级与端到端错误处理的分割
USB2.0 仅在每个事务进行端到端级别的错误检测、恢复和流控。超速在端到端(数据包重试)和链路级别(头包重试)分割这些功能。这个分割的动机是:头包在每一段链路上都可能出错,如果每段都做端到端重试,主机根本不知道错误发生在哪一段;把头包重试下沉到链路层,每对端口自己负责本段链路的头包可靠性,端到端只关心数据包顺序号和重试。
常见的实现思路是,链路层维护一个重试计数器,头包 CRC 校验失败时由发送端链路伙伴重发,不惊动上层;数据包的 CRC-32 失败或者顺序号不匹配,才触发端到端重传,主机从出错的顺序号开始重新发。这个设计让错误恢复的粒度更细,也解释了为什么超速在高噪声环境下的有效吞吐比 USB2.0 稳——链路级重试的延迟远小于端到端重试。
5. 传输类型与电源管理:四种传输在超速下的参数变化
5.1 控制传输:512 字节包和 32 个顺序号的约束
控制传输的目的与 USB2.0 完全相同,用来做设备初始化和管理,访问设备描述符,向设备请求操作。每个设备需要启动默认控制管道作为消息管道。超速控制端点有固定的最大控制传输数据包大小 512 字节,最大突发大小为 1,控制传输不支持突发。设备描述符的 bMaxPacketSize 域必须记录 09H,解码规则是 2 的 9 次方等于 512。默认控制管道必须支持最大顺序值 32,顺序值范围 0 到 31。
控制传输带宽需求方面,设备没有方法为控制管道指示想要的带宽。主机通过权衡所有控制管道总线访问需求与挂起事务来提供最好情况。超速需要保留有效总线带宽给控制管道:控制传输事务可能与其他功能端点事务一同安排;控制传输的重试不能优先于其他最好情况事务;如果有控制传输和块传输为多个端点挂起,根据主机控制器相关的公平规则选择服务;控制端点传输流控制事件时,主机把它从活动端点移除,收到 ERDY 后恢复。
5.2 块传输与流:1024 字节包、1 到 16 突发和多路流
块传输用来支持海量数据通信设备,传输能使用任何可用的超速带宽。块传输端点最大数据包大小设为 1024 字节,突发大小范围 1 到 16,所有超速块传输端点支持 0 到 31 的顺序值。主机需要支持任何超速 bulk 端点和所有 bulk 传输突发大小,确保发送给端点的数据包数量不超过描述符定义的最大包大小,突发数据包不超过记录的最大突发大小。
块传输功能端点发送的数据包数据域必须总是小于等于 1024 字节。如果块传输数据量超过 1024,突发事务中所有数据包除了最后一次都必须是 1024 字节,最后一次是剩下的不足 1024 字节的数据。块传输可以持续多个总线事务处理,在端点已经准确传输完期待数据、传输了短包、或者应答 STALL 握手包时完成。
流(stream)是超速对 bulk 的协议级扩展,在标准块传输管道中多路传输多个独立逻辑数据流。没有流能力时,多路数据只能靠应用层分帧;有了流,每个流有独立流 ID,端点侧维护多组顺序号和缓存。这个能力对存储设备尤其有用,UASP 协议就靠流来区分不同命令队列。
5.3 中断与同步传输:服务间隔、突发上限和 PING 机制
中断传输用来支持带有轮询服务间隔需要高可靠性传输小数量数据的设备。超速中断传输保证最大服务间隔,保证在下一次服务间隔中尝试重新传输。每个服务周期为中断端点尝试中断传输,保留一定带宽保证尝试。一旦传输成功,本周期内不再尝试,直到下一个轮询服务周期。如果端点以 NRDY 或者指示不能接收的 ACK 应答,主机不会在此次服务中再尝试,除非收到 ERDY,然后主机必须在收到通知后的 2 个服务周期内服务端点。
中断端点最大突发大小是 3,支持突发大于 1 时最大包大小只能 1024,等于 1 时可以是 1 到 1024 任意值。周期性端点(中断和同步)可以被分配到超速总线可利用带宽的 80%。中断端点通过端点描述符指定想要的服务间隔,周期是 2 的 (bInterval-1) 次方乘以 125 微秒,bInterval 范围 1 到 16。
同步传输用来支持能容忍错误、周期性轮询服务的传输流。超速跟 USB2.0 一样不发送起始帧,时序信息通过 ITP 发给设备。同步端点最大突发大小 16,支持突发大于 1 时最大包大小只能 1024,等于 1 时可以是 0 到 1024 任意值。同步端点在每个服务周期能进行 3 次最大突发为 16 的事务处理,即 3 乘 16 乘 1024 字节。同步端点总是在轮询服务周期中从包顺序号 0 开始发送数据包,顺序号 0 到 31 循环,不支持重试,不能应答流控。
电源管理方面,超速允许同步传输服务间隔中自动进入低耗电状态,不服务的时间段进入低功耗。SS 主机在服务间隔前发送 PING 包到目标同步设备,允许开始同步传输之前转变成电源活动状态。设备以 PING_RESPONSE 包应答通知主机所有连到设备的链路在活动状态。这个机制防止同步传输穿过非活动链路时因唤醒延迟导致数据在服务周期内没到达。另外,超速提供 Latency Tolerance 消息,设备可以通知主机自己进入低功耗状态前可容忍的延迟时间;设备还可以发送 Interval Adjustment 消息给主机调整间隔 125 微秒一直到正负 13.333 微秒。USB2.0 电源管理总是主机导向的,超速链路两端都支持电源管理,每个链路能独立进入低电源状态。
6. 避坑与排查:超速调试里最容易翻车的几个点
6.1 枚举成功但传输掉速:突发大小被描述符写死
现象:设备枚举正常,控制传输能通,但 bulk 传输速率远低于预期,抓包看到每个事务只发一个数据包。
原因:SuperSpeed Endpoint Companion Descriptor 里的最大突发大小字段被写成 1,或者主机侧读描述符时解析偏移错了,导致调度器按突发 1 处理。USB2.0 没有这个描述符,从 USB2.0 移植过来的固件经常漏填或者填错。
解决:用总线分析仪抓配置阶段的描述符响应,核对 Companion Descriptor 的 bMaxBurst 字段。主机侧检查解析代码里描述符长度判断,超速端点描述符后面必须跟 Companion Descriptor,长度不对就跳过会导致读到默认值。
6.2 NRDY 之后设备再也不发 ERDY:端点缓存状态僵持
现象:IN 传输中途设备回 NRDY,主机把端点移出调度队列,然后永远等不到 ERDY,传输挂死。
原因:设备侧端点缓存空的时候发 NRDY,但应用层往缓存写数据后没有触发 ERDY 发送逻辑。常见于固件里 ERDY 发送条件写成“缓存从空变非空”,而应用层是一次性填满缓存,状态跳变被漏掉。
解决:在设备固件里把 ERDY 触发条件改成电平判断而不是边沿判断,缓存非空且端点处于 NRDY 状态就发 ERDY。主机侧加超时兜底,超时后发一个零长度 ACK TP 探测端点状态,而不是无限等待。
6.3 链路训练失败但设备供电正常:LMP 交互卡在电源状态切换
现象:设备插入后供电正常,但主机识别不到超速设备,USB2.0 部分能枚举,超速部分无响应。
原因:链路训练阶段 LMP 交互卡在低功耗状态切换。超速链路两端独立控制电源状态,集线器把任意下游端口可见的最高链路电源状态传递到上游端口。如果下游端口在低功耗状态收到包,集线器要把它转出低功耗并通知主机和设备,这个带内通知如果丢失,链路就僵在低功耗。
解决:查链路训练日志里 LMP 的电源状态切换序列,确认 U1/U2/U3 退出握手有没有完成。硬件上查差分线终端匹配,信号完整性差会导致 LMP 丢包。软件上确认设备固件没有在训练完成前主动请求低功耗。
6.4 同步传输周期性丢数据:ITP 时间戳没对齐
现象:同步传输大部分周期正常,但每隔一段时间丢一个服务周期的数据,抓包看到 ITP 间隔不均匀。
原因:主机发送 ITP 的时机受调度器影响,如果 ITP 发送被高优先级事务挤占,设备侧基于 ITP 的时间戳推算服务周期就会偏移。同步端点不支持重试,丢一个周期就是丢数据。
解决:检查主机控制器 ITP 发送的优先级配置,ITP 应该独立于数据事务调度。设备侧不要只依赖单个 ITP 推算周期,用多个 ITP 做滑动平均。如果用了 PING 机制,确认 PING_RESPONSE 在服务间隔前足够早到达。
6.5 控制传输 STALL 后恢复失败:ClearFeature 发到了错误端点
现象:控制传输某个请求回 STALL,主机发 ClearFeature 清除停止条件,但后续传输仍然 STALL。
原因:ClearFeature 的 wIndex 字段里端点号或方向填错。超速端点用设备地址、端点号、方向三参数寻址,方向位在 wIndex 的最高位,从 USB2.0 移植的代码经常把方向位漏掉或者填反。
解决:抓控制传输的 setup 包,核对 ClearFeature 请求的 wIndex 字段。端点号在低 4 位,方向在 bit 7,IN 端点置 1,OUT 端点置 0。默认控制管道端点 0 不需要方向位。
7. 设计取舍:为什么超速不把可靠性全压在端到端
超速把错误处理拆成链路级头包重试和端到端数据包重试两层,这个取舍值得多想一步。USB2.0 时代,所有错误都在事务级别处理,主机发令牌、等数据、校验、重试,逻辑简单但延迟高。超速如果照搬这套,突发传输就没法做——你不可能在等每个包端到端确认的同时还连续发 16 个包。把头包可靠性下沉到链路层,每对端口自己保证本段链路的头包正确,端到端只负责数据顺序号和重传,突发才能跑起来。
但这个设计有代价:链路层重试对上层不可见,调试时如果只看端到端日志,会漏掉链路层的反复重试。我一般会这么理解:链路层重试是“局部修补”,端到端重试是“全局重来”,局部修补多了说明物理层有问题,全局重来多了说明端点缓存或流控有问题。两者的计数器应该分开看,混在一起看就会误判。
另一个取舍是电源管理的粒度。USB2.0 电源管理总是主机导向,主机决定什么时候挂起设备。超速让链路两端都支持电源管理,每个链路独立进入低电源状态,集线器把下游最高链路电源状态传到上游。这个设计的收益是空闲链路能快速进入低功耗,代价是唤醒路径变长,同步传输必须靠 PING 机制提前唤醒。常见的实现思路是在链路层维护一个电源状态机,U0 活动、U1/U2 浅睡、U3 深睡,状态迁移由两端协商,任何一端有包要发就发起退出低功耗握手。
验证这套机制是否 work,最直接的办法是用总线分析仪抓一次完整的枚举加 bulk 传输,看四个东西:描述符里 Companion Descriptor 的突发大小、事务里 ACK TP 的顺序号字段、NRDY/ERDY 的配对、ITP 的间隔。这四个数据点对上了,说明协议层和链路层的配合是通的;对不上,就按第 6 章的排查顺序往下走。从那以后我每次调超速设备,都强制先抓一遍描述符和头包 CRC 错误计数,再谈吞吐优化——血泪经验是,很多“性能问题”其实是描述符填错或者链路重试在偷偷吃带宽。希望帮到你。
本文还有配套的精品资源,点击获取