在说枚举之前,先大概说说USB的一种传输模式——控制传输。这种传输在USB中是非常重要的,它要保证数据的正确性,在设备的枚举过程中都是使用控制传输的。控制传输分为三个阶段:①建立阶段。②数据阶段。③确认阶段。建立(setup)阶段都是由USB主机发起,它是一个setup数据包,里面包含一些数据请求的命令以及一些数据。如果建立阶段是输入请求,那么数据阶段就要输入数据;如果建立阶段是输出请求,那么数据阶段就要输出数据。如果在数据阶段,即便不需要传送数据,也要发一个0长度的数据包。数据阶段过后就是确认阶段。确认阶段刚好跟数据阶段相反,如果是输入请求,则它是一个输出数据包;如果是输出请求,则它是一个输入数据包。确认阶段用来确认数据的正确传输。
好了,下面我们来看看枚举的详细过程。
首先,USB主机检测到USB设备插入后,就会先对设备复位。设备复位后,USB主机就会对地址为0的设备发送获取设备描述符的标准请求。所有的USB设备在总线复位后其地址都为0,这样主机就可以跟那些刚刚插入的设备通过地址0通信。主机在建立阶段发出获取设备描述符的输入请求,设备收到该请求后,在数据阶段将设备描述符返回给主机。主机在成功获取到一个数据包的设备描述符后并且确认没有什么错误后(注意:有些USB设备的端点0大小不足18字节(但至少具有8字节),而标准的设备描述有18字节,在这种情况下,USB设备只能暂时按最大包将部分设备描述符返回,而主机在成功获取到前面一部分描述符后,就不会再请求剩下的设备描述符部分,而是进入设置地址阶段),就会返回一个0长度的确认数据包给设备。
然后主机再对设备复位一下,接下来就会进入到设置地址阶段。这时USB主机发出一个设置地址的请求,并在后面跟着一个0长度的数据输出包。地址包含在建立包中,具体的地址USB主机会负责管理,它会分配一个唯一的地址给新的设备。USB设备在收到地址后,返回0长度的应答包,设备在收到这个0长度应答包的ACK之后,就可以起用新的地址了。这样设备就分配到了一个唯一的设备地址,以后主机就通过它来进行访问该设备。
然后主机再次获取设备描述符,这次跟第一次可能有点不一样,这次需要获取完全部的18个字节的设备描述符。当然,如果你的端点0缓冲大于18字节的话,那就跟第一次的情形一样了。
接下来,主机就会获取配置描述符。配置描述符总共为9字节。主机在获取到配置描述符后,根据里面的配置集合总长度,再获取配置集合。配置集合包括配置描述符,接口描述符,端点描符等等。
如果有字符串描述符的话,还要获取字符串描述符。另外HID设备还有HID描述符等。使用BUS HOUND以及通过串口返回信息,很容易看到具体的过程。总之是主机请求什么,你的程序就响应什么。
下面这些数据是使用BUS HOUND抓的,这个是在WIN2000下抓到的,如果在WINXP下,就看不到设置地址之前的数据。
写了注释下面的部分就是主机和设备之间的数据通信,而其它的则是主机跟根集线器之间的通信数据。
Device Phase Data Description Cmd.Phase.Ofs(rep) Delta
------ ----- -------------------------------------------------- ---------------- ------------------ -----
14.1 DI 02 . 1.1.0 3.4sc
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 2.1.0(2) 17us
14.0 DI 01 01 01 00 .... 2.2.0 27us
14.0 CTL 23 01 10 00 01 00 00 00 CLEAR FEATURE 4.1.0 110us
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 5.1.0(2) 56us
14.0 DI 01 01 00 00 .... 5.2.0 14us
14.0 CTL 23 03 04 00 01 00 00 00 SET FEATURE 7.1.0 109ms
14.1 DI 02 . 8.1.0 90ms
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 9.1.0 23us
14.0 DI 03 01 10 00 .... 9.2.0 22us
14.0 CTL 23 01 14 00 01 00 00 00 CLEAR FEATURE 10.1.0 8us
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 11.1.0 19ms
14.0 DI 03 01 00 00 .... 11.2.0 34us
/第一次获取设备描述符请求//
14.0 CTL 80 06 00 01 00 00 40 00 GET DESCRIPTOR 12.1.0 239us
/第一次返回设备描述符//
/由于该端点缓冲只有16字节,因此只读到16字节///
14.0 DI 12 01 10 01 00 00 00 10 65 10 36 21 01 00 00 00 ........e.6!.... 12.2.0 67ms
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 13.1.0 49us
14.0 DI 03 01 00 00 .... 13.2.0 27us
14.0 CTL 23 03 04 00 01 00 00 00 SET FEATURE 14.1.0 24us
14.1 DI 02 . 15.1.0 112ms
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 16.1.0 21us
14.0 DI 03 01 10 00 .... 16.2.0 25us
14.0 CTL 23 01 14 00 01 00 00 00 CLEAR FEATURE 17.1.0 8us
设置地址,地址为2/
14.0 CTL 00 05 02 00 00 00 00 00 SET ADDRESS 18.1.0 19ms
///第二次获取设备描述符请求//
14.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR 19.1.0 40ms
第二次返回了全部的18字节设备描述符//
14.0 DI 12 01 10 01 00 00 00 10 65 10 36 21 01 00 00 00 ........e.6!.... 19.2.0 75ms
02 01 .. 19.2.16
获取9字节的配置描述符/
14.0 CTL 80 06 00 02 00 00 09 00 GET DESCRIPTOR 20.1.0 44us
返回9字节的配置描述符/
可以看到配置描述符集合长度为0x20(即32)字节//
14.0 DI 09 02 20 00 01 01 00 80 dd .. ...... 20.2.0 50ms
14.0 CTL a3 00 00 00 02 00 04 00 GET STATUS 21.1.0 214ms
14.0 DI 00 01 00 00 .... 21.2.0 34us
//再一次获取设备描述符//
17.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR 22.1.0 3.8ms
/返回设备描述符
17.0 DI 12 01 10 01 00 00 00 10 65 10 36 21 01 00 00 00 ........e.6!.... 22.2.0 67ms
02 01 .. 22.2.16
//获取配置描述符///
17.0 CTL 80 06 00 02 00 00 09 00 GET DESCRIPTOR 23.1.0 28us
///返回配置描述符///
17.0 DI 09 02 20 00 01 01 00 80 dd .. ...... 23.2.0 50ms
/获取0x20字节的配置描述符集合,包括配置描述符,接口描述符,端点描述符等/
17.0 CTL 80 06 00 02 00 00 20 00 GET DESCRIPTOR 24.1.0 30us
/返回了0x20字节的配置描述符集合/
17.0 DI 09 02 20 00 01 01 00 80 dd 09 04 00 00 02 08 06 .. ............. 24.2.0 83ms
50 00 07 05 82 02 40 00 00 07 05 02 02 40 00 00 P.....@......@.. 24.2.16
获取字符串描述符的语言ID///
17.0 CTL 80 06 00 03 00 00 02 00 GET DESCRIPTOR 25.1.0 29us
///设备返回语言ID,第一字节表示语言ID的长度///
17.0 DI 04 03 .. 25.2.0 45ms
根据长度获取4字节的字符串描述符的语言ID///
17.0 CTL 80 06 00 03 00 00 04 00 GET DESCRIPTOR 26.1.0 17ms
设备返回完整的语言ID
17.0 DI 04 03 09 04 .... 26.2.0 53ms
/获取索引为2的字符串描述符///
17.0 CTL 80 06 02 03 09 04 02 00 GET DESCRIPTOR 27.1.0 27us
//设备返回字符串描述符,第一字节为该字符串描述符的长度//
17.0 DI 12 03 .. 27.2.0 46ms
/根据长度获取索引为2的字符串描述符///
17.0 CTL 80 06 02 03 09 04 12 00 GET DESCRIPTOR 28.1.0 23us
/设备返回完整的0x12字节字符串描述符/
17.0 DI 12 03 32 00 30 00 37 00 31 00 30 00 39 00 38 00 ..2.0.7.1.0.9.8. 28.2.0 70ms
32 00 2. 28.2.16
设置配置///
17.0 CTL 00 09 01 00 00 00 00 00 SET CONFIG 29.1.0 28us
设置接口
17.0 CTL 01 0b 00 00 00 00 00 00 SET INTERFACE 30.1.0 22ms
//获取最大逻辑单元/
17.0 CTL a1 fe 00 00 00 00 01 00 GET MAX LUN 31.1.0 158us
17.0 DI 00 . 31.2.0 53ms
///USB大容量存储设备的命令块封包(CBW)
17.2 DO 55 53 42 43 c8 58 25 81 24 00 00 00 80 00 06 12 USBC.X%.$....... 32.1.0 3.0ms
00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 ...$........... 32.1.16
以下是我在做USB键盘时,通过串口发回的调试信息,从这也可以看到枚举的过程。
系统启动................................
断开USB连接........................
USBD12芯片初始化
设置地址.....................
地址为: 0
连接USB...............
USB总线复位................................
USB总线挂起................................
USB总线复位................................
USB总线挂起................................
USB总线复位................................
USB标准请求................................
获取描述符................................
获取设备描述符................................
USB总线复位................................
USB标准请求................................
设置地址.....................
地址为: 2
USB标准请求................................
获取描述符................................
获取设备描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
获取配置描述符................................
USB标准请求................................
获取描述符................................
获取字符串描述符................................
获取语言ID................................
USB标准请求................................
获取描述符................................
获取字符串描述符................................
获取设备序列号................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
获取配置描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
获取字符串描述符................................
获取语言ID................................
USB标准请求................................
获取描述符................................
获取字符串描述符................................
获取产品字符串................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
获取字符串描述符................................
获取语言ID................................
USB标准请求................................
获取描述符................................
获取字符串描述符................................
获取产品字符串................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
获取设备描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
获取配置描述符................................
USB标准请求................................
获取描述符................................
获取配置描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
获取描述符................................
USB标准请求................................
设置配置................................
USB类请求................................
设置空闲................................
USB标准请求................................
获取描述符(从接口)..............................
获取报告描述符................................
USB标准请求................................
获取描述符(从接口)..............................
USB标准请求................................
获取描述符(从接口)..............................
USB标准请求................................
获取描述符(从接口)..............................
USB标准请求................................
获取描述符(从接口)..............................
USB标准请求................................
获取描述符(从接口)..............................
USB标准请求................................
获取描述符(从接口)..............................
USB标准请求................................
获取描述符(从接口)..............................