USB 教程合集 - 总目录
本讲目标
学完这一讲,你应该能够理解 USB 描述符为什么分成多个层级,知道每一层描述符负责什么,并且能够拿到一段设备描述符后,尝试逐个字段解释它的含义。
一、为什么 USB 设备需要描述符?
上一讲我们说过,USB 通信是 Host 主导的问答过程。
电脑通过 USB 线连接一个设备后,并不能直接知道它是键盘、鼠标、U 盘,还是一个自定义的单片机设备。
那么,电脑是怎么知道的?
答案就是:描述符(Descriptor)。
你可以把描述符理解成设备递交给电脑的一份资料。电脑通过读取这些资料,了解设备的基本信息、功能组织方式,以及后续应该通过哪些端点进行通信。
但这里有个问题:一个设备可能有多个功能,例如一个 USB 设备既是键盘,又是鼠标,还提供一个虚拟串口。如果所有信息都塞进同一个描述符,不仅难以管理,也不利于操作系统分别加载驱动。
因此,USB 使用了分层的描述符体系。
二、描述符是一棵树
可以先记住下面这张结构图:
Device Descriptor(设备描述符) | └── Configuration Descriptor(配置描述符) | ├── Interface Descriptor(接口描述符) | | | ├── 类专用描述符(可选) | └── Endpoint Descriptor(端点描述符) | └── Interface Descriptor(另一个接口) | └── Endpoint Descriptor这里需要注意:配置描述符后面并不是只跟一个接口。一个配置可以包含多个接口,每个接口也可能包含多个端点。
下面分别看看这些描述符的作用。
2.1 设备描述符:先告诉电脑你是谁
设备描述符(Device Descriptor)描述整个 USB 设备的基本信息,例如:
- USB 规范版本相关信息。
- 默认控制端点 EP0 的最大包长。
- 厂商 ID(VID)。
- 产品 ID(PID)。
- 设备版本号。
- 设备描述符中指定的设备类信息。
- 设备拥有多少个配置。
其中,VID 和 PID 经常出现在设备管理器和 USB 抓包工具中。
2.2 配置描述符:设备有哪些工作配置?
配置描述符(Configuration Descriptor)描述一种设备配置,包含配置值、接口数量以及整个配置描述符集合的总长度等信息。
大多数简单 USB 设备只有一个配置,但协议允许设备提供多个配置。
这里的“配置”不是简单的应用软件配置文件,而是 USB 设备向 Host 提供的一组接口和端点组织方式。
2.3 接口描述符:这个接口负责什么功能?
接口描述符(Interface Descriptor)描述一个接口的基本信息,包括:
- 接口编号。
- 备用设置编号。
- 该接口拥有的端点数量。
- 接口类、子类和协议代码。
例如,HID 接口通常会声明 HID 类相关信息。CDC 设备则可能通过多个接口共同实现虚拟串口功能。
操作系统经常根据接口信息决定如何匹配驱动,而不是只看整个设备的 VID/PID。
2.4 端点描述符:具体从哪里传输数据?
端点描述符(Endpoint Descriptor)描述一个端点的地址、方向、传输类型、最大包长,以及相应的间隔字段。
例如,一个设备可能有:
Endpoint 0x81:IN 方向 Endpoint 0x01:OUT 方向这里的0x81和0x01是示意值,不代表某个真实设备。
端点地址的最高位用于表示方向:最高位为 1 表示 IN,为 0 表示 OUT;低四位表示端点编号。
IN 和 OUT 的方向始终以 Host 为参照。
- IN:Device → Host。
- OUT:Host → Device。
端点的详细含义,我们在第4讲中继续讨论。
三、VID 和 PID 到底是什么?
VID 是 Vendor ID,即厂商标识;PID 是 Product ID,即产品标识。
可以简单理解为:
- VID 用于标识厂商。
- PID 用于区分该厂商管理的不同产品。
但它们并不是 USB 设备功能的全部描述。即使两个设备使用不同的 VID/PID,它们也可能都属于 HID 键盘;反过来,同一个产品也可能通过不同接口提供多种功能。
正式产品使用 VID/PID 时,需要遵守相应的分配和授权规则,不能随意冒用其他厂商的标识。
四、如何读懂一段设备描述符?
假设我们看到下面这段数据:
bLength 18 bDescriptorType 01h bcdUSB 0200h bMaxPacketSize0 64 idVendor 0x1234 idProduct 0x5678 bNumConfigurations 1这是一段简化的示意信息,并不是完整的设备描述符,也不代表真实厂商。
逐项理解:
bLength = 18:标准设备描述符长度为 18 字节。bDescriptorType = 01h:表示这是设备描述符。bcdUSB = 0200h:表示该设备描述符声明的 USB 规范版本为 2.00。bMaxPacketSize0 = 64:表示默认控制端点 EP0 的最大数据包长度为 64 字节。该字段的合法取值与设备速度和规范要求有关。idVendor:厂商 ID。idProduct:产品 ID。bNumConfigurations = 1:设备提供一个配置。
需要注意,USB 描述符中的多字节数值通常使用小端序存储。例如,原始字节34 12对应的数值是0x1234。
如果抓包工具已经把原始数据解析成字段,就可以直接阅读解析结果;如果拿到的是十六进制原始数据,则需要结合字段长度、字段顺序和字节序进行分析。
五、为什么电脑会分多次读取描述符?
配置描述符后面还可能包含接口描述符、端点描述符和类专用描述符,因此整个配置集合的长度并不是固定的。
Host 通常可以先读取配置描述符开头的 9 字节,从wTotalLength得知整个配置描述符集合的长度,然后再读取完整内容。
所以,如果你在抓包中发现电脑先读取一小段配置描述符,随后又读取更长的一段,这通常是正常的枚举过程,不一定是设备发生了异常。
六、本讲小结
本讲只需要记住一个核心结构:
设备描述符描述整个设备,配置描述符组织接口,接口描述符描述功能,端点描述符描述数据通道。
不要急着记住所有字段。拿到一段真实设备的描述符时,先找到它属于哪一层,再逐个解释字段,会容易得多。
留给下一讲的问题
电脑已经知道设备有哪些端点,那么端点究竟是什么?为什么键盘、U 盘和摄像头使用的传输方式不一样?
下一讲,我们来认识端点、管道与 USB 四种传输类型。