2、USB基础协议:USB总线拓扑、传输类型(控制、批量、中断、等时)、设备枚举流程、描述符结构
USB(Universal Serial Bus)自1996年诞生以来,已从最初的外设连接接口演变为嵌入式系统、消费电子、工业控制乃至汽车电子中不可或缺的总线标准。对于从事USB主机控制器驱动与设备驱动底层开发的工程师而言,理解USB协议的基础架构是进行寄存器配置、DMA描述符管理、中断处理以及电源管理的前提。本章将围绕USB总线拓扑、四种传输类型、设备枚举流程以及描述符结构四个核心维度展开,帮助读者建立从物理层到协议层的完整认知框架。
2.1 USB总线拓扑结构
核心原理:分层星型拓扑
USB总线采用分层星型(Tiered Star)拓扑结构,与PCIe的树状结构或I2C的总线型结构有本质区别。整个拓扑由主机控制器(Host Controller)、根集线器(Root Hub)、外部集线器(External Hub)以及功能设备(Function Device)四类节点构成。主机控制器通常集成在SoC内部(如高通的DWC3、Synopsys的DWC2),通过UTMI+或ULPI接口与片外PHY相连。根集线器是主机控制器内部逻辑的一部分,提供物理端口(Port)用于连接外部设备。
USB 2.0规范规定最大支持7层拓扑(含根集线器层),即从主机到设备最多经过5个外部集线器。每经过一个集线器,信号会经历一次中继(Repeater)处理,引入约几十纳秒的传播延迟。对于高速(480Mbps)设备,过长的链路会导致眼图闭合,因此驱动工程师在调试高速设备时,应优先考虑减少级联层数。
| 拓扑层级 | 节点类型 | 典型延迟 | 地址分配 |
|---|---|---|---|
| 第1层 | 根集线器端口 | < 10ns | 固定地址0 |
| 第2-6层 | 外部集线器 | 30-80ns/级 | 1-127动态分配 |
| 第7层 | 功能设备 | 取决于设备 | 1-127动态分配 |
判断方法:识别设备连接状态
在驱动层面,判断设备是否连接通常依赖两个信号:D+和D-上的上拉/下拉电阻。全速/高速设备在D+线上接1.5kΩ上拉电阻,低速设备在D-线上接上拉电阻。主机控制器通过检测端口上的电平状态(J状态或K状态)来判断设备插入或拔出。在xHCI驱动中,端口状态寄存器(PORTSC)的CSC位(Connect Status Change)会触发中断,驱动需读取当前连接状态并启动枚举流程。
lsusb -t可以查看当前USB拓扑树。若发现设备挂载在过深的层级(如第6层),应检查是否因线缆过长或集线器级联过多导致信号质量下降。
案例拆解:多设备级联场景下的带宽分配
假设一个工业控制主板需要同时连接USB摄像头(等时传输,带宽占用80%)、条码扫描枪(中断传输)和U盘(批量传输)。摄像头被挂载在第3层集线器下,U盘在第2层。此时主机控制器需要为每个端点分配带宽。对于全速总线(12Mbps),等时传输最多可占用90%的帧时间(Frame Time),但必须预留至少10%用于控制传输。驱动工程师在初始化时需计算各端点的带宽占用,若超出预算,应调整设备挂载位置或降低摄像头帧率。
常见误区与总结
- 误区1:认为所有USB设备都直接连接在根集线器上。实际嵌入式系统中,板载集线器(如USB3503)常用于扩展端口数量,但会增加枚举延迟。
- 误区2:忽略拓扑深度对高速信号的影响。USB 2.0高速模式对信号质量敏感,建议高速设备不超过3层拓扑。
- 总结:拓扑结构决定了地址分配、带宽共享和电源管理策略。驱动开发时应先通过硬件原理图确认设备挂载路径,再编写对应的枚举和带宽管理逻辑。
2.2 USB传输类型详解
核心原理:四种传输类型的特性对比
USB协议定义了四种传输类型,每种类型针对不同的应用需求设计,在带宽保证、错误重传、延迟特性上各有取舍。理解这些差异是配置端点描述符和选择DMA传输模式的基础。
| 传输类型 | 典型应用 | 带宽保证 | 错误重传 | 最大包长(FS) | 最大包长(HS) |
|---|---|---|---|---|---|
| 控制传输 | 枚举、命令配置 | 10%保留 | 硬件自动重试 | 8/16/32/64B | 64B |
| 批量传输 | U盘、MTP、网络 | 无保证 | 硬件重传 | 8/16/32/64B | 512B |
| 中断传输 | 鼠标、键盘、HID | 保证延迟 | 硬件重传 | ≤64B | ≤1024B |
| 等时传输 | 音频、视频、实时数据 | 保证带宽 | 无重传 | ≤1023B | ≤1024B |
判断方法:根据应用需求选择传输类型
选择传输类型时,需回答三个问题:数据是否允许丢失?(等时传输允许丢包,其他不允许);延迟是否敏感?(中断和等时对延迟敏感);数据量大小?(批量传输适合大块数据)。例如,一个温度传感器每秒上报一次数据,数据量仅4字节,应选择中断传输而非批量传输,因为批量传输的调度优先级较低,可能因总线繁忙而延迟。
案例拆解:USB音频设备的等时传输配置
一个USB音频播放器(UAC2.0规范)需要播放48kHz/16bit/双声道音频,数据速率为48k×2×2=192KB/s。在高速模式下,每个微帧(125μs)需传输192KB/s×0.125ms=24字节。驱动需配置等时端点,最大包长设为192字节(考虑抖动余量),并启用自适应同步(Adaptive)模式。实际调试中,若发现音频卡顿,可通过cat /proc/asound/card0/pcm0p/sub0/hw_params查看实际采样率,若偏差超过±100ppm,需调整反馈端点(Feedback EP)的采样值计算逻辑。
常见误区与总结
- 误区1:将中断传输理解为“周期性中断”。实际上中断传输是保证延迟的轮询传输,主机在每帧/微帧中固定查询设备。
- 误区2:在批量传输中追求极低延迟。批量传输的调度优先级最低,当总线繁忙时延迟可能达到数十毫秒。
- 总结:四种传输类型是USB协议的核心调度单元。驱动开发时,端点描述符中的bmAttributes字段决定了传输类型,需与硬件DMA通道的FIFO深度、中断触发方式匹配。
2.3 设备枚举流程
核心原理:从连接到配置的完整状态机
设备枚举是USB设备从物理插入到可被系统识别使用的完整过程,涉及主机控制器驱动(HCD)与设备端固件的多次交互。整个流程遵循严格的状态机:Attached → Powered → Default → Address → Configured。每个状态转换由特定的总线事件或控制传输触发。
- 连接检测:设备插入后,D+/D-电平变化触发根集线器端口状态改变中断。
- 设备复位:主机向端口发送SE0信号(D+和D-同时拉低)持续至少10ms,设备检测到复位后进入默认状态(Default),地址为0。
- 获取设备描述符:主机向地址0发送GET_DESCRIPTOR请求,读取前8字节(含bMaxPacketSize0),用于确定端点0的最大包长。
- 分配地址:主机发送SET_ADDRESS请求,设备进入地址状态(Address),此后所有通信使用新地址。
- 读取完整描述符:主机再次发送GET_DESCRIPTOR获取完整设备描述符、配置描述符、接口描述符和端点描述符。
- 配置设备:主机发送SET_CONFIGURATION请求,设备进入配置状态(Configured),此时功能端点可用。
判断方法:枚举失败时的排查路径
当设备无法被识别时,可按以下顺序排查:
- 检查
dmesg中的USB核心日志,确认是否检测到设备插入(usb 1-1: new high-speed USB device)。 - 使用USB分析仪(如Beagle USB 480)抓取总线数据,确认主机是否发送了SET_ADDRESS请求。
- 若设备返回STALL,说明设备固件未正确处理请求,需检查设备描述符中的bMaxPacketSize0是否与实际硬件FIFO匹配。
- 若设备反复复位,可能是电源供电不足(VBUS跌落),检查稳压电路。
案例拆解:高速设备枚举失败分析
某嵌入式设备使用USB3300 PHY连接高速USB设备,但设备始终无法枚举。通过逻辑分析仪抓取D+和D-信号,发现设备插入后,主机发送了Chirp K序列(高速握手),但设备未响应Chirp K-序列。进一步检查发现,设备端固件在检测到复位后,未在1ms内启动高速握手,导致主机降级为全速模式。修复方法:在设备固件的中断处理中,缩短复位检测到Chirp K启动的延迟至500μs以内。
常见误区与总结
- 误区1:认为枚举过程中设备地址始终为0。实际上SET_ADDRESS成功后,设备立即切换到新地址,后续请求必须使用新地址。
- 误区2:忽略bMaxPacketSize0的重要性。若该值配置错误(如全速设备配置为64字节),主机将无法正确解析后续描述符。
- 总结:枚举流程是USB驱动开发中最常调试的环节。建议在驱动中增加枚举状态机日志,记录每个步骤的耗时和错误码,便于快速定位问题。
2.4 描述符结构
核心原理:描述符的层级关系与字段含义
USB描述符是设备向主机声明自身能力的数据结构,采用TLV(Type-Length-Value)格式。所有描述符的第一个字节为bLength(长度),第二个字节为bDescriptorType(类型)。标准描述符类型包括:设备描述符(Type 1)、配置描述符(Type 2)、字符串描述符(Type 3)、接口描述符(Type 4)、端点描述符(Type 5)。
设备描述符是根节点,包含USB版本号、设备类、厂商ID(VID)、产品ID(PID)等关键信息。配置描述符下可包含多个接口描述符,每个接口描述符下可包含多个端点描述符。这种层级关系决定了设备的逻辑功能划分。例如,一个USB复合设备(如带麦克风的摄像头)可能包含两个接口:一个用于视频流(等时端点),一个用于音频流(等时端点+中断端点)。
| 描述符类型 | 关键字段 | 典型值 | 作用 |
|---|---|---|---|
| 设备描述符 | idVendor, idProduct, bcdUSB | 0x1234, 0x5678, 0x0200 | 识别设备身份 |
| 配置描述符 | bNumInterfaces, bMaxPower | 1, 50 (2.5W) | 定义功耗和接口数 |
| 接口描述符 | bInterfaceClass, bInterfaceSubClass | 0xFF (vendor), 0x00 | 声明功能类型 |
| 端点描述符 | bEndpointAddress, wMaxPacketSize | 0x81, 0x0200 | 定义传输通道 |
判断方法:解析描述符时的注意事项
在驱动中解析描述符时,需注意以下要点:
- 字节序:所有多字节字段采用小端序(Little-Endian),如idVendor=0x1234在总线上传输为0x34, 0x12。
- 配置描述符集合:GET_DESCRIPTOR(Configuration)返回的不只是配置描述符本身,而是包含接口、端点和类特定描述符的完整集合。
- 字符串描述符:使用UTF-16LE编码,索引从1开始,索引0返回语言ID列表。
案例拆解:自定义HID设备的描述符配置
设计一个自定义HID设备(如游戏手柄),需在接口描述符中设置bInterfaceClass=0x03(HID类),并添加HID描述符(Type 0x21)。HID描述符中需声明报告描述符的长度。实际调试中,若主机无法加载HID驱动,可通过cat /sys/kernel/debug/usb/devices查看内核解析的描述符信息,确认bInterfaceProtocol是否正确(如0x01表示键盘,0x02表示鼠标)。
const常量,并放在独立的链接段中。这样便于通过USB分析仪比对实际发送的数据与预期是否一致。
常见误区与总结
- 误区1:配置描述符中的bMaxPower字段单位是2mA,而非1mA。若需设备消耗500mA,应填写250。
- 误区2:端点描述符中的bEndpointAddress的bit7表示方向(1=IN,0=OUT),bit3-0表示端点号。不能将0x81理解为端点8输入,而是端点1输入。
- 总结:描述符是USB设备与主机通信的“身份证”。驱动开发人员应熟练掌握各描述符的字段偏移和含义,建议在代码中定义对应的结构体并添加静态断言(_Static_assert)确保长度正确。
通过本章的学习,读者应能够理解USB总线拓扑的物理意义,掌握四种传输类型的适用场景,熟悉设备枚举的完整状态机,并能独立解析和构建设备描述符。这些基础知识是后续编写主机控制器驱动(如xHCI Ring管理)和设备驱动(如UVC、UAC类驱动)的基石。在实际项目中,建议结合USB规范文档(USB 2.0 Spec Chapter 9)和具体硬件手册进行交叉验证,以加深理解。