USB枚举和HID枚举实例

在说枚举过程之前,先把一些必须了解的说明白

一.USB包结构和分类

     包的共同特点是,都是以同步域开始,接着是PID,最后以EOP结束,而设备端则靠SEI(串行接口引擎,硬件上实现)来进行这些底层的处理,包括CRC的校验之类的东东。

    8位的PID,PID0~PID3,用于表示包,高四位进行取反,进行校验

    各种包的如下:

    令牌类:OUT,IN ,SOF,SETUP

    数据类:DATA0,DATA1,DATA2,MDATA

    握手类:ACK,NCK,STALL,NYET

    特殊类:PRE,ERR,SPLIT,PING

    令牌包用于启动一次USB传输,这些IN,OUT都以主设备而言的

    SETUP建立控制传输过程

    令牌包的结构为,

    同步域+PID+7位地址+4位端点号+5位CRC校验+EOP

    数据包的结构为,

    同步域+PID+字节0~字节N+CRC+EOP

    在数据包中,我们看到有DATA0和DATA1,这实际上形成了一种数据纠错机制。

    在数据发送成功或者接收时,数据包类型切换,如果检测到包类型没有切换,说明刚刚的数据传输没有发送成功。否则表示成功接收到数据了,虽然也有握手包来说明问题。

   握手包

   同步域+包标识+EOP

 

二.事务

   不能简单的通过包来进行数据传输,所以由不同的包组织而成事务,就是所谓的transcation

一个事务通常由三个不同类型的包组成,令牌包,数据包,握手包

令牌包启动事务,握手包返回信息,数据包传输数据,方向由令牌包决定

我们知道USB中有4中传输类型,批量传输,等时传输,中断传输和控制传输,控制传输一般用于总线的枚举过程。

控制传输事务有三个过程,包括建立过程,数据过程,状态过程

其余三种传输对应一个事务

我们挑控制传输说明问题,其余就简单了

   这里有个有趣的东西,setup只能使用DATA0,在数据传输过程中,一旦数据传输方向发生变化,就会认为进入了状态过程,数据传输的第一个过程必须是DATA1包,每次传输正确之后是在DATA0和DATA1中切换,而状态事务只能使用DATA1包。

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包。

接下来获取配置描述符、配置集合、字符串描述符、报告描述符的过程差不多,这里就不再叙述了。

具体可以看下图:

 

 

USB枚举和HID枚举实例_第1张图片

上面说的前两个过程在BUS bound中是看不到的,上图来自网络

具体的上面已经说得很清楚了

其中红线的部分就是设置的地址,

 

set_address请求的结构为

bmrequestType 0x00

SET_ADDRESS 0x05

wValue 设备地址(上面提到的0x0002,设备地址一般从地址0开始分配

wIndex 0x0000

wLength 0x0000

具体可以看文档

 

四.USB HID设备(鼠标)的枚举过程

USB枚举和HID枚举实例_第2张图片

 

以上的截图是通过BUS BOUND得到的,不过没有set_address前的枚举过程而已。

(1)   取得设备描述符    get device descriptor

80 06 00 01 00 00 12 00

80         标准请求 数据传输方向 设备到主机

06         取得描述符请求Get Descriptor

0100     01表示的设备描述符 00描述符的索引

0000     读取描述符时 字符串的语言ID

0012     18字节 需要返回取得的字节数

(2)   设备描述符

12 01 10 01 00 00 00 08 3a 09 10 25 00 01 01 02 00 01

12         18字节 设备描述符长度

01         设备描述符

0110     USB设备的USB协议为1.1

00          设备使用的类代码 一般这里定义为0 协议在接口描述符中定义

00         子类代码

00         设备使用的协议

08         端点0的最大包长

093a      厂商的ID

2510            产品ID              这两个值可以在设备属性中看到

0100      版本号1.0

01          描述厂商的字符串的索引值    

02          产品的字符串索引值

00          设备的序列号字符串索引值   0表示没有序列号字符串

01          设备有多少种配置      

(3)   取得配置描述符 Get Descriptor

80 06 00 02 00 00 09 00

这里取得描述符(1)类似

02 表示 取得配置描述符

00 09 希望取得的配置描述符长度

(4)   配置描述符

09 02 22 00 01 01 04 a0 32

09         length

02         configuration descriptor

0022     配置描述符总长度34字节

01         该配置所支持的接口数01 鼠标是一个接口,一个接口代表一个功能

01                配置值为01      set config时,如果找到与配置值匹配的,则设置为此配置

04          配置字符串的索引值

a0                 a0对应的二进制位10100000 D71是必须的,D61表示为设备自供电,为0表示总线供电 D51表示支持远程唤醒 0表示不支持 其余位为0

32                    表示需要从总线获得的最大电流量 2mA*0x32=100mA

(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该接口使用的端点数   只是用一个端点(中断端点)不包括0端点

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  下级描述符的类型 0x22报告描述符 0x23物理描述符

003e 下级描述符的长度 当有多个下级描述符时,上述两值交替下去

 

端点描述符

07  05 81 03 04 00 0a

07   端点描述符的长度

05  端点描述符类型

81  端点的地址 10000001 D7表示传输方向 1为输入 D6~D4 reserved D3~D0为端点号 端点号为01

03    端点的属性 D1~D0表示端点的传输类型 11表示为中断传输 10批量传输 01等时传输 00控制传输 非等时传输D7~D20

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    条目类型 0 主条目 1为全局条目 2为局部条目 3reserved

D1~D0 bSize      条目后面所跟数据的字节数

00 0字节 01 1字节 10 2字节 11 4字节

你可能感兴趣的:(USB枚举和HID枚举实例)