在说枚举过程之前,先把一些必须了解的说明白
一.USB包结构和分类
二.事务
一个事务通常由三个不同类型的包组成,令牌包,数据包,握手包
令牌包启动事务,握手包返回信息,数据包传输数据,方向由令牌包决定
我们知道USB中有4中传输类型,批量传输,等时传输,中断传输和控制传输,控制传输一般用于总线的枚举过程。
控制传输事务有三个过程,包括建立过程,数据过程,状态过程
其余三种传输对应一个事务
我们挑控制传输说明问题,其余就简单了
USB设备的检测机制,在前面已经说过了,说个有意思的二次枚举的应用(因为重新上电之后就会有BUS的枚举设备过程)当设备插入之后,它先被识别成一个设备,该设备负责从主机上下载固件到设备的RAM内,然后设备将上拉电阻断开(模拟拔下,设备未断电,可以对口线进行操作就可以了),接着重新连接上拉电阻,当重新检测到设备时,使用的是已经下载的固件了,这就是不用烧录器的好处,只要改固件程序。
三.USB设备枚举过程
下面是USB设备枚举的过程
1.主机发起第一个控制传输(获取设备描述):
(1)主机SETUP包(发往地址0端点0)、主机数据包(请求设备描述符)、设备握手包ACK。
设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在端点0输入缓冲区准备好设备描述符。
(2)数据过程,主机先发一个IN令牌包、设备发一个数据包(这个数据已经准备好,SIE收到IN令牌后,直接送到总线上,用户此时不干预)、主机发ACK包。
此时SIE产生端点0数据输入中断,表明主机已经取走了设备所准备的数据,用户也可以在该中断处理程序中作自己的处理。(如清理操作等)
此时,主机只接受一次数据,最少8个字节。如果用户数据没有发完,又在控制端点输入缓冲区,准备了数据,主机也不理会。
(3)状态过程:主机发OUT包(通知设备要输出)、主机发0字节状态数据包(这个是0字节,表明自己收到设备描述符)、设备发握手ACK包。
此时设备不会产生端点0数据输出中断,此时没有数据。
2、枚举过程中,第二个来回:设置地址。
第一个来回成功以后,主机再次复位总线。进入地址设置控制传输阶段。
(1)主机SETUP包(发往地址0端点0)、主机数据包(请求设置地址)、设备握手包ACK。所以SETUP包后面都会跟一个表明主机SETUP目的的数据包,要么GET,要么SET。
设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在根据主机发来的地址写入自己的地址控制寄存器。
(2)数据过程,本次传输没有数据。
(3)状态过程:主机发IN包(通知设备要返回数据)、设备发0字节状态数据包(表明地址设置已经成功)、主机发握手ACK包(地址设置已经生效)。
此时设备不会产生端点0数据输入中断,此时没有数据。
3、枚举过程中,第三个来回:主机使用新地址获取完整的设备描述符。
主机采用新地址发起第一个控制传输:
(1)主机SETUP包(发往新的地址端点0)、主机数据包(请求设备描述符)、设备握手包ACK。
设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在端点0输入缓冲区准备好设备描述符。
(2)数据过程,主机先发一个IN令牌包、设备发一个数据包(这个数据已经准备好,SIE收到IN令牌后,直接送到总线上,用户此时不干预)、主机发ACK包。
此时SIE产生端点0数据输入中断,表明主机已经取走了设备所准备的数据,用户可以该中断处理程序中要做如下处理:如果一次没有将描述符送完,要再次将剩下的内容填充端点0输入缓冲区。
第二次数据传输:主机再发一个IN令牌包、设备发一个数据包、主机发ACK包。
此时SIE再次产生端点0数据输入中断,如果数据已经发完了。这里就不处理了。进入状态过程。
(3)状态过程:主机发OUT包(通知设备要输出)、主机发0字节状态数据包(表明自己收到设备描述符)、设备发握手ACK包。
接下来获取配置描述符、配置集合、字符串描述符、报告描述符的过程差不多,这里就不再叙述了。
具体可以看下图:
上面说的前两个过程在BUS bound中是看不到的,上图来自网络
具体的上面已经说得很清楚了
其中红线的部分就是设置的地址,
set_address请求的结构为
bmrequestType 0x00
SET_ADDRESS 0x05
wValue 设备地址(上面提到的0x0002,设备地址一般从地址0开始分配)
wIndex 0x0000
wLength 0x0000
具体可以看文档
四.USB HID设备(鼠标)的枚举过程
以上的截图是通过BUS BOUND得到的,不过没有set_address前的枚举过程而已。
(1)
80 06 00 01 00 00 12 00
80
06
0100
0000
0012
(2)
12 01 10 01 00 00 00 08 3a 09 10 25 00 01 01 02 00 01
12
01
0110
00
00
00
08
093a
2510
0100
01
02
00
01
(3)
80 06 00 02 00 00 09 00
这里取得描述符(1)类似
02 表示 取得配置描述符
00 09 希望取得的配置描述符长度
(4)
09 02 22 00 01 01 04 a0 32
09
02
0022
01
01
04
a0
32
(5)
80 06 00 02 00 00 22 00
这里的0x0022就是在上一个描述中得到的
(6)
包括配置描述符,接口描述符 ,类描述符(如果有的话),端点描述符
09 02 22 00 01 01 04 a0 32 09 04 00 00 01 03 01 02 00 09 21 11 01 00 01 22 3e 00 07 05 81 03 04 00 0a
其中配置描述符
09 02 22 00 01 01 04 a0 32
接口描述符
09 04 00 00 01 03 01 02 00
09 接口描述符长度
04 接口描述符
00 接口的编号 编号为0(如果有很多接口 这里会逐个显示)
00 接口的备用编号为0
01该接口使用的端点数
03 该接口所使用的类 USB鼠标为HID类,编码03
01 该接口所使用的子类01 这个子类支持BIOS引导启动的子类
02 协议 01为键盘 02 为鼠标
00 接口所使用的字符串索引值00 为没有
类描述符(HID类)
09 21 11 01 00 01 22 3e 00
09 类描述长度
21 HID描述符
0111 使用的HID协议1.11
00
01 下级描述符的数量 至少为1,报告描述符或者物理描述符
22
003e 下级描述符的长度 当有多个下级描述符时,上述两值交替下去
端点描述符
07
07
05
81
03
0004 支持最大的包长度 最大长度为4 对于高速设备这里有新的解释
0a
(7)设置配置
00 09 01 00 00 00 00 00
00 设置
09 设置配置
0001 配置的值为1,这里与上面配置1相匹配,选中该配置
0x0000 wIndex
0x0000 wLength
(8) set IDLE请求
这个请求要求设备在没有新的事件发生时,不要从中断端点中返回数据。
HID中类请求有set_idle ,get_idle,get_report,set_report HID鼠标可以不用理会
(9)get descriptor
这里取的是接口描述符
81的后五位D4~D0表示请求的接收者
0 设备
1 接口
2 端点
3 其他
4~31 保留
81 06 00 22 00 00 7e 00
22 请求的描述符为HID的报告描述符 0x23为物理描述符
007e 请求的报告描述符长度(不懂为啥是这个,需要看主设备驱动和文档)
(10)报告描述符
HID中有短条目和长条目之分
一般使用短条目 包括1字节的前缀和可选的数据字节组成
一般是加1字节 可选字节为0,1,2,4字节
前缀的1字节的结构为
D7~D4 bTag
D3~D2 bType
D1~D0 bSize
00 0字节 01 1字节 10 2字节 11 4字节