USB 2.0等时传输的DATA2与MDATA:从PID原理到抓包实战

发布时间:2026/10/9 14:54:53
USB 2.0等时传输的DATA2与MDATA:从PID原理到抓包实战
记得头一回在USB 2.0总线上看到0x87这个PID时我差点以为是抓包工具抽风了。做USB开发这些年DATA0和DATA1几乎无处不在从控制传输的数据阶段到批量传输、中断传输的数据翻转能叫得上名字的基本就是这两种数据包。可当你在高速等时传输里第一次撞见DATA20x87和MDATA0x4F时心里难免犯嘀咕这俩是哪来的是设备乱发还是分析仪解析错了这篇内容就是想把这些“生面孔”彻底讲清楚并结合实际的抓包数据带你看一看它们到底出现在哪个场景里适合那些平时做USB驱动、嵌入式固件、或者用协议分析仪排查设备兼容性的开发者参考。1. 为什么做了几年USB开发你可能从没在抓包里见过DATA21.1 绝大多数抓包工具只到URB层到不了“包”这一层很多人觉得“抓包”就是用Wireshark、USBPcap或者Linux下的usbmon把总线上跑的数据全部记录下来然后挨个看。真实情况是这些工具抓的基本都是**URBUSB Request Block**级别的记录也就是主机控制器驱动和USB驱动之间交换的请求信息而不是总线上真正电信号层面的数据包。Wireshark里显示的“USB URB”包含设备地址、端点号、传输类型、传输方向和长度但往往不会告诉你这次传输用的是DATA0还是DATA1更不用说DATA2和MDATA了。这就解释了一个困扰很多人的现象明明自己在写USB驱动抓包也抓了一大堆可从来没看见过PID。不是你眼力不行是你的观察位置压根不在链路层。要看到真正的数据包PID需要USB协议分析仪或者分析仪导出的总线级pcap文件再用Wireshark这类工具打开。很多厂商的中低端分析仪配套软件可以导出包含所有总线包的pcap这种文件里才有完整的Token包、Data包和Handshake包。1.2 DATA0和DATA1的翻转机制靠的是“状态位”而非固定编号在讲DATA2之前有必要先把DATA0/DATA1为什么够用这件事说透。USB的可靠传输控制、批量、中断依赖一个叫“数据切换位”的机制。发送方和接收方各自维护一个1bit状态初始都是0。发送第一个数据包时用DATA0接收成功后双方把状态翻成1第二个数据包用DATA1成功后再翻回0第三个又用DATA0。这样做的目的很朴素接收方收到一个数据包后会检查PID是否和自己期望的一致。如果收到重复包PID肯定对不上于是知道发送方没收到上一轮的ACK会自动重传或丢弃。这套机制的精髓在于翻转不是靠“当前是第几个包”来算的而是靠双方各自维护的期望位。只要你手里的状态位和对方一致DATA0/DATA1就能一直正确转下去。所以正常情况下批量传输、中断传输、控制传输的数据阶段确实只需要两种PID就足够。1.3 等时传输没有ACKPID就是唯一的“节拍器”等时传输Isochronous走的是另一条路数据按确定的时间节奏发出去发送方不等待接收方的确认接收方也不回ACK。这样做的好处是延迟低、带宽稳定适合音频、视频这类不允许重传的数据流。代价也很明显——丢了包没人通知你。没有ACK重传机制PID的作用就从“确认同步”变成了“边界同步”。等时传输的数据包之间必须靠PID的变化让接收方知道“这是新一轮数据了”。全速12Mbps等时传输很多设备只能用DATA0/DATA1交替因为一毫秒帧里通常就塞一个数据事务。可USB 2.0高速模式加入后一个微帧里能塞多个事务DATA0/DATA1两个编号就不够表达了这才有了DATA2和MDATA的用武之地。2. DATA2和MDATA是为高速高带宽等时设计的“补充角色”2.1 微帧和Mult字段一个125us的窗口能塞多少数据USB 2.0高速模式的速率是480Mbps为了降低延迟规范把原来1ms的帧拆成了8个125us的微帧。普通的等时端点每个微帧只能做一个事务即使这个事务能一次传1024字节按8000个微帧每秒算理论峰值也就8MB/s多一点这对高质量视频流来说明显不够。高带宽等时端点High-Bandwidth Isochronous Endpoint就是为了解决这个问题出现的。关键信息藏在端点描述符的wMaxPacketSize字段里低11位表示单个事务能传的最大字节数第11位和第12位合起来表示“每个微帧最多几个事务”这个值常被称为Mult或Transactions per Microframe。Mult可以是1、2或者3。如果Mult3单个微帧里就能塞进最多3个等时事务一个服务间隔1ms的理论数据量立刻涨了三倍。读描述符时要注意一个细节对高速等时端点来说低11位读出来是0不代表包长为0很多协议栈会把它解释成1024字节。如果你用Wireshark打开分析仪导出的数据它通常已经帮你换算好了但在自己写代码解析描述符时这一点很容易踩坑。2.2 四种数据PID一览把0xC3/0x4B/0x87/0x4F放一起看USB数据包PID本身自带校验信息。我一开始也看不太明白为什么DATA0是两个字节值0xC3而不是想象中的0x00。实际上PID字节的低四位是对高四位取反得到的校验所以完整字节看起来有点“乱”。常见数据PID如下PID名称完整字节常见用途DATA00xC3普通传输的数据翻转偶数包也用于等时单事务DATA10x4B普通传输的数据翻转奇数包控制传输状态阶段通常固定用它DATA20x87高速高带宽等时微帧内多事务时的标识MDATA0x4F高速高带宽等时微帧内中间数据包的标识从这个表能看出来DATA2和MDATA不是厂商私有的“特殊PID”而是USB 2.0规范里白纸黑字定义的标准数据PID类型。它们和高速等时传输强绑定在控制、批量、中断传输里基本不会出现。如果你在Wireshark里过滤usb.pid能搜到0x87和0x4F基本可以断定这条总线上正在跑高带宽等时传输。2.3 MDATA到底在“修正”什么中间包与收尾包的区分逻辑“MDATA”这个名字很容易让人误解我一度以为它是某种修正后的数据或者厂商用来做私有的校验包。其实它的真正作用是让接收方知道“这个数据包只是这一微帧数据的中间部分后面还有数据没送完”。举个例子一个设备的Mult字段声明了3按说每个微帧都应该发3个事务。但实际数据流不可能永远刚好填满3个事务到了某个时刻数据量可能只够发2个甚至1个事务。如果没有额外的PID规则接收方收到前2个包后根本判断不出“这一微帧到底还有没有第3个包”。容易丢帧、容易错位。所以很多控制器会这样处理把后面还有续包的中间包标记为MDATA把本微帧的最后一个包标记为DATA2。接收方看到MDATA就知道继续等看到DATA2就明白这一微帧的事务到头了。至于更具体的组合方式不同控制器厂商的实现会有差异。有的在Mult3且3个事务都是满包时直接用DATA0/DATA1/DATA2循环编号有的更倾向于统一用MDATA接DATA2收尾。规范给出了框架但没把细节锁死到一种排列所以实战里你会看到不止一种PID序列。2.4 一个容易混淆的点DATA2不是“第三种toggle”有朋友会理所应当地认为DATA0、DATA1、DATA2就是toggle位从0、1、2依次走下去像三进制计数器一样。这个理解放到普通批量传输里是不对的。批量传输没有“DATA2”这个状态协议栈固件如果收到DATA2大概率会当成错误包处理。DATA2只在高速等时的特定场景里出现而且它的出现通常和端点描述符里的Mult有关而不是因为传输序号已经累计到了“第2个包”。把DATA2理解成“微帧收尾标记”至少在实际调试中更管用。当你分析一份抓包记录时看到DATA2不应该关心“前面刚过了几个DATA0”而应该关心“它是不是某一个微帧里的最后一个数据包”。这个视角切换到位后再去看抓包时序会清晰很多。3. 实战抓包让DATA2与MDATA在总线上现身3.1 准备一套能看到链路层PID的抓包环境先说结论要想在抓包里直接看到DATA2和MDATA普通软件抓包是不够的。USBPcap和usbmon只能给你URB层面的事件它们不是从电信号层面去解析完整USB包所以看不到Token、Data、Handshake的分层结构。你需要一台USB协议分析仪或者至少能导出总线级pcap的抓包方案。分析了仪最方便的地方在于它会记录每条总线上最原始的事务SOF令牌、IN/OUT令牌、数据包PID、握手包全部带上精确时间戳。很多分析仪的PC软件可以把整个会话导出成标准pcap导出后用Wireshark打开就可以利用Wireshark的USB dissector去看每个包的PID、端点、长度。如果暂时手里没有分析仪也有个折中办法先抓URB级数据通过端点的Mult字段和URB数量间接推测某条流是不是高带宽等时再用分析仪精确验证PID分配。实战中可以先用usbmon或USBPcap确认设备端点和传输类型再决定是否要上分析仪。3.2 读出设备的wMaxPacketSize先确认它是不是高带宽等时端点抓包之前我习惯先读一下设备描述符确认这个端点到底是不是高带宽等时。在Linux下可以这样modprobe usbmon lsusb -v -d 1234:5678把1234:5678换成你的设备实际VID:PID。重点看接口描述符里的端点信息。对高速等时端点wMaxPacketSize那块通常会出现类似“0x1800”这样的值高位的Mult字段是3低11位解析出来是1024字节。如果这里读出来只是普通等时比如Mult1那你基本不用指望抓包能看到DATA2和MDATA因为设备根本没启用高带宽模式。在Windows下USBPcap抓到的URB事件也会带上端点配置信息可以从Wireshark的“Frame”或“USB URB”详情里找到端点属性。判断标准是一致的传输类型是IsochronousMult大于1。3.3 用Wireshark过滤并定位0x87和0x4F拿到总线级pcap后Wireshark里可以这样过滤usb.pid 0x87 usb.pid 0x4f0x87是DATA20x4F是MDATA。这两个过滤器能帮你在成百上千个包里快速定位关键PID。不同版本的Wireshark对PID字段的命名可能略有差异有的版本显示为usb.pid有的版本在USB URB层里干脆不展示PID。如果过滤没结果先检查一下文件到底是总线级pcap还是URB级pcap前者才有数据包PID。我一般还会配合端点地址过滤比如某个UVC摄像头的视频流走的是端点0x82就加上usb.endpoint_address 0x82这样能把范围缩小到同一条等时流里的数据包方便后续做微帧内的时序拆解。3.4 解一段真实抓包一个微帧里的三次IN事务下面是我在某款USB 2.0 UVC摄像头抓包记录里看到的片段我已经把时间戳和地址做了简化处理序号时间戳方向端点PID长度10.000000IN0x82DATA2102420.000135IN0x82MDATA102430.000270IN0x82DATA225640.000375微帧边界-SOF-注意看前三个数据包它们全都落在同一个125us微帧里。第1个和第2个包都是满包但PID不同一个用DATA2标记“这是本微帧的数据开端/某类中间状态”一个用MDATA标记“还有后续包”。第3个包的PID又回到了DATA2而且长度只有256字节明显是一个短包收尾表示这一微帧的数据到此为止。不同控制器下这个微帧内的排列顺序可能不一样。有的控制器会规规矩矩地用DATA0、DATA1、DATA2三连发有的则像上面这样用MDATA把中间包标识出来。排列不同不代表设备有问题只要接收方解析规则一致数据就能正确拼接。真正要警惕的是那种明明Mult3却连续几个微帧只发1个包、而且PID毫无规律可循的情况那通常是设备固件对等时传输的处理存在缺陷。4. 踩坑记录与调试建议从认错PID到定位丢帧4.1 把0x87当成厂商私用PID的排查经历有段时期我在调一块视频采集卡设备出帧断断续续于是在分析仪导出的pcap里看到了大量0x87。当时的直觉是“这不会是厂商拿了保留PID做私有功能吧”然后到处翻协议库和资料折腾了一个晚上没结果。后来把Wireshark的USB dissector更新到新版本PID才从“Unknown”变成DATA2顺着这个线索再去对端点Mult终于确定是一条高带宽等时流在跑。这个经历提醒我看到不认识的PID先查规范里的Data PID定义不要直接怀疑设备乱发。0x87对应DATA20x4F对应MDATA都是标准PID。如果哪个抓包工具把它们解析成“Reserved”一般不是USB设备的问题而是抓包工具版本太旧。4.2 “mult读出来是0”是怎么回事还有一次某个设备描述符里等时端点的wMaxPacketSize高位字段读出来是0。我差点以为这个端点每微帧只能做一个事务差点把后续的带宽估算全算错。后来仔细翻协议才发现对于高速等时端点Mult字段为0时的处理在不同主机控制器上并不统一有些控制器会视同Mult1有些则在枚举阶段就报错。如果遇到描述符里Mult0但设备实际数据流量明显超过单事务极限的情况建议不要轻易相信这个字段直接用抓包工具统计一个微帧内的事务数量来判断真实行为。抓包不会骗人描述符有时候会。4.3 Wireshark版本不同PID字段显示还不一样Wireshark对USB总线包的支持在不同版本之间有明显差异。老版本可能只认识USB URB层打开总线级pcap后直接把Data包当成不透明数据新版本则能解析出PID、CRC、握手状态等更多信息。如果你发现usb.pid过滤器没有反应先检查两件事一是pcap来源是否真的是链路层抓包二是Wireshark版本是否需要升级。我个人习惯同时用分析仪自带软件和Wireshark对照。分析仪软件擅长精确统计和总线时序可视化Wireshark擅长写过滤器和批量分析。两边数据的PID定义是一致的互相印证比较稳妥。4.4 高带宽等时调试的几个实用建议调试高速高带宽等时传输最容易碰到的问题不是“抓不到包”而是包太多、不知道从哪看起。我的建议是先从SOF包入手把微帧边界画出来再数每个微帧里到底有几个数据事务。只要画出这个时间线Mult配置对不对、短包在哪里收尾、哪个微帧丢了包基本一目了然。遇到丢帧问题时不要一上来就怀疑USB控制器。先看数据包是否连续再看PID序列是否规律最后再查固件里等时调度是不是漏了某个微帧。对依赖等时传输的采集类设备来说数据包短包提前收尾往往意味着上游数据喂得不够快这一点在调试UVC摄像头时尤其常见。另外一点经验高速等时传输对USB线缆质量非常敏感。同样的设备和程序换一根质量差的线缆抓包里就能看到频繁的CRC错误和PID乱序。所以分析高带宽等时问题前先排除物理层干扰能少走很多弯路。DATA2和MDATA这两种PID第一次看见可能会觉得陌生但它们其实就是USB 2.0在高速时代为等时传输留下的两道标记。只要理解了微帧和Mult的配合逻辑再抓几次总线级数据它们就会和DATA0/DATA1一样自然。下次你在pcap里看到0x87和0x4F时至少可以笃定地说这条总线上正有一段高带宽等时数据在飞。