7.13.2. USB 设备枚举简介¶
重要
一个 USB 设备是否能正常使用,依赖于设备有没有被正确配置。USB 设备在插入端口时会触发枚举,在这个过程中 Host 读取设备的各种描述符,并且下发相应的配置。80% 的 USB 问题是发生在设备枚举过程当中的,所以 USB 开发人员必须对此有详细了解。
7.13.2.1. 背景¶
USB使用以下方法来满足多种类型的数据在一条共享通道上传输:
时间延迟
。从时间维度上把数据传输切成多个时间片,在每个时间片内绝大部分份额 (最多 80%) 优先传输对时间延迟有要求的数据,如Interrupt Transfers
、Isochronous Transfers
。在时间片剩下的额度内传输对时间延迟没要求的数据,如Control Transfers
、Bulk Transfers
。完整性校验
。对需要保证数据完整性的数据加上了CRC 校验,接收端使用 ACK 来知会发送端正确接收,如果没有收到 ACK 发端会尝试重发 3 次。
如上图,USB从时间维度上把数据传输切成多个时间片:
Frames
。Low-speed 和 Full-speed 的时间切片大小为 1ms,USB 控制器每1ms重新调度一下传输。Microframes
。High-speed 的时间切片大小为 125us,USB 控制器每125us重新调度一下传输。
这个时间切片,和操作系统上 Schedule Tick 的概念是一样的。
在数据格式传输上又会进一步细分:
Transfer
。每个时间片的所有传输称之为一个 Transfer,或者为一个 Frames/Microframes。Transcation
。根据某次数据传输的目的,一个 Transfer 可以分成多个 Transcation 事务。Packet
。数据传输的最小单位,一个 Transcation 可能由多个 Packet 组成。
7.13.2.2. USB 协议传输格式¶
Transfer
/Frame
(传输): 从时间的维度看,USB通信是由一系列的Transfer
(传输)组成的。包括四种传输类型:Control Transfers
控制传输Isochronous Transfers
同步传输Interrupt Transfers
中断传输Bulk Transfers
批量传输
Transaction
(事务): 每一个Transfer
(传输)又可以分成不同的 Transaction,具体的 Transaction 类型为以下3种:IN/Read/Upstream Transaction
输入 (Host) 事务OUT/Write/Downstream Transaction
输出 (Host) 事务Control Transaction
控制事务
Packet
每一次 Transaction 又由不同的 Packets 所组成Token Packet
Data Packet
Handshake Packet
Special Packet
Field
每一个 Packet 又由不同的 Field 组成:Sync
同步域PID
标识域ADDR
设备地址域ENDP
端点域FRAM
帧号域DATA
数据域CRC
校验域
7.13.2.2.1. Packet¶
通常一个Packet由5个部分组成。
PID
: Packet IDADDR
: Device AddressEP
: Endpoint NumberPayload DATA
CRC
: 5/16 bits CRC
以下是四种 Packet 的具体格式。
7.13.2.2.1.2. Data Packet¶
Data Packets 主要由三部分组成:
Packet ID
(DATA1/DATA0, toggle), -Payload data
,CRC16
。
7.13.2.2.1.3. Handshake Packet¶
Handshake Packets主要有4种:
ACK
: 返回成功NAK
: Device 忙,或者没有什么需要执行。STALL
: Device 出错NYET
: Host only, Not ready.
7.13.2.2.1.4. Special Packet¶
PRE
: 由 Host 向 Hub 发送,指示下一个 packet 是 low speedSPLIT
: Host only, split transactionERR
: Host only, 由 Hub 向 Host 发送,指示在一次 split transaction 中出错PING
: Host only, check status
7.13.2.2.2. Transaction¶
USB Transactions主要由四种Packet组成: Token Packet
、 Data Packet
、 Handshake Packet
和 Special Packet
。但通常的一次 Transaction
由 3 个 Packets 组成: Token
-> Data
-> Handshake
。
7.13.2.2.2.1. IN/Read/Upstream Transaction¶
IN Transaction 主要用于 Device 向 Host 发送数据。
Host 向 Deivce 发送一个
IN Token Packet
Device 如果忙,则回复
NAK Packet
, Host 在收到NAK Pakcet
之后则持续向 Device 发送IN Token Packet
。当 Device 准备好时,回复
Data Packet
, Host在收到Data packet
之后回复一个ACK handshake
。
7.13.2.2.2.2. OUT/Write/Downstream Transaction¶
OUT Transaction主要用于Host向Device发送数据。
Host 向 Deivce 发送一个
OUT Token Packet
和一个DATA Packet
。Device忙,则回
NAK
, Host则会复发OUT
和DATA
Device在成功收到数据后,回
ACK handshake packet
。
7.13.2.2.2.3. Control Transaction¶
Control Transaction
最多由3个Stage组成: Setup stage
、 Data stage
、 Status stage
。其中 Data stage
不是必需的,有的 control transaction
没有 data stage
。
7.13.2.2.2.3.1. Setup Stage (Transaction
)¶
Setup stage 中的 setup packet 只会出现在 control transaction 中,Device 必须回 ACK, 不能回 NAK。
7.13.2.2.2.3.2. Data Stage (Transaction
)¶
Data Stage 可以有若干的 Data transaction。这个 stage 只有在 Host 和 Device 需要 data 传输时才会存在,通常 setup stage 的 payload 可以 cover , 这个时候就不需要 data stage。
7.13.2.2.2.3.3. Status Stage (Transaction
)¶
以下是 control read transaction 和 control write tranasaction 分别对应的 status stage。
7.13.2.3. USB 枚举通讯过程¶
USB 设备在正常工作之前,第一件要做的事情就是枚举。枚举的作用就是让设备从上述状态图中的 Powered
逐步进入 Configured
模式。
USB 枚举的主要目的是读出设备的所有配置信息。
7.13.2.3.1. USB 设备枚举总体流程¶
是让 Host 认得这个USB设备,并且为该设备准备资源,建立好主机和设备之间的数据传递机制。一个设备枚举的过程分为如下8步:
获取设备描述符
Host/Hub 通过数据线上拉电阻的阻值变化检测到新设备接入。Host 等待 100ms 以保证设备电源稳定。
Host 向 device 发 Bus Reset 使得设备进入 default 状态,从此之后,设备可以响应默认地址 0。
Host 请求 Device 发送 Device Descriptor 的前 64 个字节。
复位
Host 在收到 Device Descritptor 的前 8 个字节后,再次向 Device 发出 Bus Reset。
设置地址
Host 发送一个 Set Address 命令给 Deivce,从此 Device 有个通信地址,不再使用默认地址 0 进行通信。
再次获取设备描述符
Host 请求获取完整的 Device Descritpor, 总计 18 字节。
获取配置描述符
Host 请求获取 9 个字节的 Configuration Descriptor 以了解 Configuration Descriptor 的总大小。
Host 请求 255 字节的 Configuration Descritpor。
获取接口,端点描述符
获取字符串描述符
选择设备配置
下图是接入一个 USB 鼠标之后完整的枚举过程,后面详细逐步分析:
7.13.2.3.2. Tranfser 0 (Control Read) (Get Device Descriptor)¶
Transfer 0
的作用是 Get Device Descriptor
,它是一个 Control Read
类型的 Transfer
。其中:
Transaction 0
:Setup Stage
Transaction 1~3
:Data Stage
Transaction 4
:Status Stage
7.13.2.3.2.1. Transaction 0 (Setup Stage)¶
Transfer 0
的第一个事务 Transaction 0
如上图。可以看出,这是一个 Control
事务,设备要执行的命令就是 GET_DESCRIPTOR
也就是获取描述符。其中分为三个包:
Packet 134
第一个为令牌包Setup Token
(主机传输给设备)Packet 135
第二个为数据包Setup Data
(主机传输给设备)Packet 136
第三个为握手包ACK
(设备传输给主机)
7.13.2.3.2.1.1. Packet 135 (Setup Data)¶
主要分析一下第二个包数据包中的DATA字段,包含的就是命令。Data中总共有 8 字节,每字节的含义如下 (USB协议报文是小端模式):
在 Linux kernel 中,使用一个结构体来表示:
struct usb_ctrlrequest {
__u8 bRequestType;
__u8 bRequest;
__le16 wValue;
__le16 wIndex;
__le16 wLength;
} __attribute__ ((packed));
bmRequestType
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
0 |
bmRequestType |
1 |
0x80 |
表示 期望的数据传输方向为 设备传输给主机,这条 命令的接收者为设备。 |
bRequest
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
1 |
bmRequest |
1 |
0x06 |
代表了
一个命令,根据 |
wValue
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
2 |
wValue |
2 |
0x00 0x01 |
根据 |
2 |
|
1 |
0x00 |
想要获取的描述符 index 为 0 |
3 |
|
1 |
0x01 |
根据 |
wIndex
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
4 |
wIndex |
2 |
0x00 0x00 |
根据 |
wLength
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
6 |
wLength |
2 |
0x40 0x00 |
根据 |
7.13.2.3.2.2. Transaction 1 ~ Transaction 3 (Data Stage)¶
可以看到 Transaction 1
~ Transaction 3
联合起来从 Device 传递回了 18 个字节的数据给 Host, Transaction 1
和 Transaction 2
分别传输了 8 个字节数据, Transaction 2
传递了 2 个字节数据。
12 01 00 02 00 00 00 08
6D 04 18 C0 01 43 01 02
00 01
这 18 个字节数据就是 Transfer 0
指定要读取的 Device Descriptor
,其具体的格式如 Table 9-8
所示:
注意在这个阶段当中的几个全局 Field
的值:
ADDR
:Device Address = 0ENDP
:EndPoint = 0DATA0
/DATA1
:如果多次 Data Transaction 传输的是一段连续数据,DATA0
DATA1
连续翻转。如果多次 Data Transaction 传输的数据是不相关的,则不会翻转。
读回的具体 Device Descriptor
解析如下:
7.13.2.3.2.3. Transaction 4 (Status Stage)¶
状态阶段用于表示一次控制传输的结束。
首先发送一个 OUT 包,注意令牌包主要是指明传输类型,这里就为 OUT 类型。然后发一个大小为 0 的 DATA 包,Device 回一个 ACK 握手包。本次传输结束。
7.13.2.3.3. Reset (Powered
→ Default
)¶
Host 向 Device 发送了一个 Reset 信号,由上边的状态图可以看出,USB 由 Powered
模式进入了 Default
模式。
7.13.2.3.4. Tranfser 1 (Control No Data) (Set Device Address) (Default
→ Address
)¶
Tranfser 1
的主要作用是设置 Device Address
,USB由状态图中的 Default
模式进入了 Address
模式,是一个 Control No Data
类型的 Transfer
。其中:
Transaction 5
:Setup Stage
Transaction 6
:Status Stage
7.13.2.3.4.1. Transaction 5 (Setup Stage)¶
Packet 275
是核心,其中的 Setup Data
具体解析如下:
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
0 |
bmRequestType |
1 |
0x00 |
表示期望的 数据传输方 向为主机传 输给设备。 |
1 |
bmRequest |
1 |
0x05 |
代表了一个命令,根据 |
2 |
wValue |
2 |
0x03 0x00 |
根据 |
4 |
wIndex |
2 |
0x00 0x00 |
根据 |
6 |
wLength |
2 |
0x00 0x00 |
根据 |
7.13.2.3.4.2. Transaction 6 (Status Stage)¶
状态阶段用于表示一次控制传输的结束。
7.13.2.3.5. Tranfser 2 (Control Read) (Get Device Descriptor)¶
Transfer 2
的作用是重新获取了一遍设备描述符 Get Device Descriptor
,不同之处不是用默认地址 0 获取的,而是用刚才配置的 Device Address 0x03 来获取的。它是一个 Control Read
类型的 Transfer
。其中:
Transaction 7
:Setup Stage
Transaction 8~10
:Data Stage
Transaction 11
:Status Stage
Transfer 2
和 Transfer 0
的主要区别在于,一是有了明确的设置地址。另外:
Transfer 0
第一次获得设备描述符主要为了得到端点0可以发送的数据的大小,所以只要一个Transaction 1
的8个字节数据就可。Transfer 2
完成了整个设备描述符的读取工作。
7.13.2.3.6. Tranfser 3 (Control Read) (Get Config Descriptor)¶
Transfer 3
的作用是获取配置描述符 Get Configuration Descriptor
,它是一个 Control Read
类型的 Transfer
。其中:
Transaction 12
:Setup Stage
Transaction 13~14
:Data Stage
Transaction 15
:Status Stage
7.13.2.3.6.1. Transaction 12 (Setup Stage)¶
Packet 275
是核心,其中的 Setup Data
具体解析如下:
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
0 |
bmRequestType |
1 |
0x80 |
表示期望的数据传输方向为设备传输给 主机,这条命令的接收者为设备。 |
1 |
bmRequest |
1 |
0x06 |
代表了一个命令,根据 |
2 |
wValue |
2 |
0x00 0x02 |
根据 |
2 |
|
1 |
0x00 |
想要获取的描述符 index 为 0 |
3 |
|
1 |
0x02 |
根据 |
4 |
wIndex |
2 |
0x00 0x00 |
根据 |
6 |
wLength |
2 |
0x09 0x00 |
根据 |
7.13.2.3.6.2. Transaction 13~14 (Data Stage)¶
可以看到 Transaction 13~14
联合起来从 Device 传递回了 9 个字节的数据给 Host, Transaction 13
传输了 8 个字节数据, Transaction 14
传递了 1 个字节数据。
09 02 22 00 01 01 00 A0
32
这 9 个字节数据就是 Transfer 12
指定要读取的 Configuration Descriptor
,其具体的格式如 Table 9-10
所示:
读回的具体 Configuration Descriptor
解析如下:
7.13.2.3.6.3. Transaction 15 (Status Stage)¶
状态阶段用于表示一次控制传输的结束。
7.13.2.3.7. Tranfser 4 (Control Read) (Get 4 Descriptors)¶
Transfer 4
的作用是一次性获取了4个描述符 Get 4 Descriptors
,它是一个 Control Read
类型的 Transfer
。其中:
Transaction 16
:Setup Stage
Transaction 17~21
:Data Stage
Transaction 22
:Status Stage
从格式上看它使用的命令 Setup Data
和 Transfer 3
一样,仅仅读取长度从 0x09
改成了 0x22
,就一次性读出了 4 个描述符。 Transfer 3
的主要作用是读取 Descriptors
的总长度, Transfer 4
根据这个长度读取了所有 Descriptors
。
7.13.2.3.7.1. Configuration Descriptor¶
09 02 22 00 01 01 00 A0
32
Transaction 17~18
提供了 9 字节的 Configuration Descriptor
数据,具体内容和解析如上一节一样。
7.13.2.3.7.2. Interface Descriptor¶
09 04 00 00 01 03 01
02 00
Transaction 18~19
提供了 9 字节的 Interface Descriptor
数据,具体内容和解析如下:
Interface Descriptor
具体的格式如 Table 9-12 所示:
7.13.2.3.7.3. HID Descriptor¶
09 21 11 01 00 01
22 34 00
Transaction 19~20
提供了 9 字节的 HID Descriptor
数据,具体内容和解析如下:
HID Descriptor
具体的格式如下所示:
7.13.2.3.7.4. Endpoint Descriptor¶
07 05 81 03 05
00 0A
Transaction 20~21
提供了 7 字节的 Endpoint Descriptor
数据,具体内容和解析如下:
Endpoint Descriptor
具体的格式如 Table 9-13 所示:
7.13.2.3.8. Tranfser 5 (Control Read) (Get String 0 Descriptor)¶
Transfer 5
的作用是获取了 index 0 的 String Descriptor
,它是一个 Control Read
类型的
Transfer
。其中:
Transaction 23
:Setup Stage
Transaction 24
:Data Stage
Transaction 25
:Status Stage
7.13.2.3.8.1. Transaction 23 (Setup Stage)¶
80 06 00 03 00 00 FF 00
Packet 366
是核心,其中的 Setup Data
具体解析如下:
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
0 |
bmRequestType |
1 |
0x80 |
表示期望的数据传输方向为设备传输给 主机,这条命令的接收者为设备。 |
1 |
bmRequest |
1 |
0x06 |
代表了一个命令,根据 |
2 |
wValue |
2 |
0x00 0x03 |
根据 |
2 |
|
1 |
0x00 |
想要获取的描述符 index 为 0 |
3 |
|
1 |
0x03 |
根据 |
4 |
wIndex |
2 |
0x00 0x00 |
根据 |
6 |
wLength |
2 |
0xFF 0x00 |
根据 |
7.13.2.3.8.2. Transaction 24 (Data Stage)¶
可以看到 Transaction 24
联合起来从 Device 传递回了 4 个字节的数据给 Host。
04 03 09 04
index 0 String Descriptor
,表示的是 设备支持的语言。解析格式如下:
所以 Transaction 24
读回的 Language ID
为 0x0409
即美国英语。
7.13.2.3.9. Tranfser 6 (Control Read) (Get String 2 Descriptor)¶
Transfer 6
的作用是获取了 index 2 的 String Descriptor
,它是一个 Control Read
类型的 Transfer
。其中:
Transaction 26
:Setup Stage
Transaction 27~31
:Data Stage
Transaction 32
:Status Stage
7.13.2.3.9.1. Transaction 26 (Setup Stage)¶
80 06 02 03 09 04 FF 00
Packet 366
是核心,其中的 Setup Data
具体解析如下:
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
0 |
bmRequestType |
1 |
0x80 |
表示期望的数据传输方向为设备传输给主机,这条 命令的接收者为设备。 |
1 |
bmRequest |
1 |
0x06 |
代表了一个命令,根据 |
2 |
wValue |
2 |
0x02 0x03 |
根据 |
2 |
|
1 |
0x02 |
想要获取的描述符 index 为 2 |
3 |
|
1 |
0x03 |
根据 |
4 |
wIndex |
2 |
0x09 0x04 |
根据 |
6 |
wLength |
2 |
0xFF 0x00 |
根据 |
7.13.2.3.9.2. Transaction 27~31 (Data Stage)¶
可以看到 Transaction 27~31
联合起来从 Device 传递回了 36 个字节的数据给 Host。
24 03 55 00 53 00 42 00
20 00 4F 00 70 00 74 00
69 00 63 00 61 00 6C 00
20 00 4D 00 6F 00 75 00
73 00 65 00
index 2 String Descriptor
,表示的是一个字符串。解析格式如下:
即读回了一个 34 字节的 unicode 字符串 55 00 53 00 42 00 20 00 4F 00 70 00 74 00 69 00 63 00 61 00 6C 00 20 00 4D 00 6F 00 75 00 73 00 65 00
转成 Ascii 码为 USB Optical Mouse
。
在上几节读取 Device Descriptor
时, iProduct
字段的值为 0x2 即 index 2 String Descriptor
,所以 iProduct
就是 USB Optical Mouse
。
7.13.2.3.10. Tranfser 7 (Control Read) (Get String 1 Descriptor)¶
Transfer 7
的作用是获取了 index 1 的 String Descriptor
,它是一个 Control Read
类型的 Transfer
。其中:
Transaction 33
:Setup Stage
Transaction 34~36
:Data Stage
Transaction 37
:Status Stage
解析方式和上一节一样,最终读出了16字节的 unicode 字符串 4C 00 6F 00 67 00 69 00 74 00 65 00 63 00 68 00
转成 Ascii 码为 Logitech
。
即 Device Descriptor
中的 iManufacturer
字段为 Logitech
。
7.13.2.3.11. Tranfser 8 (Control No Data) (Set Configured) (Address
→ Configured
)¶
Tranfser 8
的主要作用是设置 Configured
,USB由状态图中的 Address
模式进入了 Configured
模式,是一个 Control No Data
类型的 Transfer
。其中:
Transaction 38
:Setup Stage
Transaction 39
:Status Stage
7.13.2.3.11.1. Transaction 38 (Setup Stage)¶
00 09 01 03 00 00 00 00
Packet 439
是核心,其中的 Setup Data
具体解析如下:
Offset |
Field |
Size |
Value |
Description |
---|---|---|---|---|
0 |
bmRequestType |
1 |
0x00 |
表示期望的数据传输方向为主机传输给设备 |
1 |
bmRequest |
1 |
0x09 |
代表了一个命令,根据 |
2 |
wValue |
2 |
0x01 0x00 |
根据 |
4 |
wIndex |
2 |
0x00 0x00 |
根据 |
6 |
wLength |
2 |
0x00 0x00 |
根据 |
7.13.2.3.12. Tranfser 9¶
好像也没干成什么事。
7.13.2.3.13. Tranfser 10¶
获得HID描述符。
7.13.2.3.14. Tranfser 11¶
是枚举成功后,两次host与device之间传输数据。这里只截了部分图。Host每个bInterval这么多时间就对device查询一下,看有没有数据要传,比如有没有键子被按下。每次都是,host向device发送一个IN令牌包,如果没有数据,device就回一个NAK。