二、枚举过程
1、用户将一个USB设备插入USB端口,主机为端口供电,设备此时处于上电状态。主机检测设备。集线器使用中断通道将事件报告给主机。
2、主机发送Get_Port_Status(读端口状态)请求,以获取更多的设备信息。返回的消息告诉主机该设备是什么时候连接的。集线器检测设备是低速运行还是高速运行,并将此信息送给主机,这是对Get_Port_Status请求的响应。
3、主机发送Set_Port_Feature(写端口状态)请求给集线器,要求它复位端口,请求集线器来重新设置端口。集线器使设备的USB数据线处于重启(RESET)状态至少10ms。
4、主机使用Chirp K信号来了解全速设备是否支持高速运行。
5、主机发送另一个Get_Port_Status请求,确定设备是否已经从复位状态退出。返回的数据有一位表示设备仍然处于重启状态。当集线器释放了重启状态,设备此时处于缺省状态,且已准备好在零端点通过缺省通道响应主机控制传输。缺省地址为00h,设备能从总线获取高达100mA的电流。
6、集线器检测设备速度
集线器通过测定哪根信号线(D+或D-)在空闲时有更高的电压来检测设备是低速设备还是全速设备。全速和高速设备D+有上拉电阻,低速设备D-有上拉电阻。
7、获取最大数据包长度
主机向address 0发送Get_Device_Deor(读设备描述符)报文,以取得缺省控制管道所支持的最大数据包长度。并在有限的时间内等待USB设备的响应。该长度包含在设备描述符的bMaxPacketSize0字段中,其地址偏移量为7,所以这时主机只需读取该描述符的前8个字节。注意,主机一次只能枚举一个USB设备,所以同一时刻只能有一个USB设备使用缺省地址0。
例:主机向设备发送一个八字节请求:80 06 00 01 00 00 40 00,设备接收到请求后产生一个中断,我们可以通过读中断寄存器知道中断源,并且可以加读最后状态寄存器来确定第一个接到的包是否为一个Setup包。当控制器处理程序判断出它是一个Get_descriptor请求时,把设备描述符的前16个字节发送到端点0缓冲区中。剩下的2个字节描述符第一次请求时不再发送。
8、主机分配一个新的地址给设备
主机通过发送一个Set_Address请求来分配一个唯一的地址给设备。设备读取这个请求,返回一个确认,并保存新的地址。从此开始所有通信都使用这个新地址。
例:当主机收到正确的前16字节描述符后,会给设备分配一个地址,我的PC分配的地址为:0x03(这个要看你的机子当时的USB接口设备数目而定) Set_Address 请求所发送的数据为:00 05 03 00 00 00 00 00,其中的03就表示主机为设备分配的地址为0x03,在以后的通信里设备就只对0x03地址作出应答。当设备产生一个接收中断后,根据所分配的地址设置设备的地址寄存器相应位。
9、主机向新地址重新发送Get_Device_Deor命令,此次读取其设备描述符的全部字段,以了解该设备的总体信息,如VID,PID。
例:主机发送设备描述符标准请求Get_descriptor:80 06 00 01 00 00 12 00,此次将要求把18个字节全部发送完。所以主机要分两次来读取。第一次读取16个字节,第二次读取两个字节,最后主机发送0表示发送完毕的应答。
10、主机向设备循环发送Get_Device_Configura
tion命令,要求USB设备回答,以读取全部配置信息。
11、主机发送Get_Device_String命令,获得字符集描述(unicode),比如产商、产品描述、型号等等。此时主机将会弹出窗口,展示发现新设备的信息,产商、产品描述、型号等。
根据Device_Deor和Device_Configuration应答,PC判断是否能够提供USB的Driver,一般win2k能提供几大类的设备,如游戏操作杆、存储、打印机、扫描仪等,操作就在后台运行。但是Win98却不可以,所以在此时将会弹出对话框,索要USB的Driver。
12、主机分配并加载设备驱动程序,这时就可能作应用中的数据传输了。
13、主机发送Set_Configuration(x)(写配置)命令请求为该设备选择一个合适的配置(x代表非0的配置值)。如果配置成功,USB设备进入“配置”状态,并可以和客户软件进行数据传输。此时,常规的USB完成了其必须进行的配置和连接工作,至此设备应当可以开始使用。不过,USB协议还提供了一些用户可选的协议,设备如果不应答,也不会出错,但是会影响到系统的功能。
14、主机为复合设备接口分配驱动程序。如果集线器检测到有过流现象,或者主机要求集线器关闭
电源,则USB总线切断设备供电电源。在这种情况下,设备与主机无法通信,但设备处于连接状态。
使用USB View 采集到的数据:
- Device Descriptor:
- bcdUSB: 0x0100
- bDeviceClass: 0xDC
- bDeviceSubClass: 0x00
- bDeviceProtocol: 0x00
- bMaxPacketSize0: 0x10 (16)
- idVendor: 0x0471
- idProduct: 0x0666
- bcdDevice: 0x0100
- iManufacturer: 0x00
- iProduct: 0x00
- iSerialNumber: 0x00
- bNumConfigurations: 0x01
- ConnectionStatus: DeviceConnected
- Current Config Value: 0x01
- Device Bus Speed: Full
- Device Address: 0x02
- Open Pipes: 4
- Endpoint Descriptor:
- bEndpointAddress: 0x81
- Transfer Type: Interrupt
- wMaxPacketSize: 0x0010 (16)
- bInterval: 0x0A
- Endpoint Descriptor:
- bEndpointAddress: 0x01
- Transfer Type: Interrupt
- wMaxPacketSize: 0x0010 (16)
- bInterval: 0x0A
- Endpoint Descriptor:
- bEndpointAddress: 0x82
- Transfer Type: Bulk
- wMaxPacketSize: 0x0040 (64)
- bInterval: 0x0A
- Endpoint Descriptor:
- bEndpointAddress: 0x02
- Transfer Type: Bulk
- wMaxPacketSize: 0x0040 (64)
- bInterval: 0x0A
复制代码
三、USB枚举实例
对2440的USB HOST进行初始化完毕(主要包括对符合OHCI规范的寄存器的初始化—总线复位、中断使能、清除中断标志、电源管理、内存指针寄存器的初始化,各种数据结构的初始化等),等待USB设备的插入,当2440检测到有设备插入,就要对设备进行枚举了。起始枚举就相当于主机和设备建立连接的过程(接头),Host向Device询问一些东西,Device将自身的设备类型,如何进行通信报告给Host,这样Host就知道怎么对Device进行操作了。
枚举的过程实际上用到而且只用到了总线的“控制传输(Control Transfer)”。这种传输方式通常用于配置/命令/状态等情形,其中的设置操作setup和状态操作status过程的数据包具有USB协议定义的数据结构,因此,控制传输只能通过消息管道进行。
一个完整的控制传输包括三个过程:
1、建立连接。
2、数据过程(可选) 。
3、状态过程。
建立连接的过程都是由Host发起,它开始于一个Setup令牌包,后面紧跟一个DATA0包。如果是控制输入传输,数据过程则为输入数据,若是控制输出传输,则数据过程是输出数据。
数据过程的可选型是指设置过程需要指定数据长度,如果指定为0,则没有数据过程。状态过程跟在数据过程之后,状态过程恰好和数据过程的数据传输方向相反,因为此阶段主要是用来确认之前两阶段的所有数据都已经正确传输了。
好了,下面就结合我的这个实例来看看枚举的详细过程:
1、控制2440向U盘发送第一个Setup包,内容是80 06 00 01 00 00 08 00,其中最后的00 08表示得到DEVICE_DCESCRIPTOR的前8个字节,因为这个包的主要目的是要获得USB Device中端点0的最大包的大小(第8个字节),所以只需要8个字节就可以了。USB Device返回的设备标识符为12 01 10 01 00 00 00 40,下面我们需要把0x40记录下来,将其放到Endpoint Descriptor数据结构的DWORD0的MPS(bit16~bit32)块中去。
2、接下来2440发送第二个Setup包,内容是00 05 01 00 00 00 00 00,这一次的作用是为USB设备分配地址。如果USB Device接收并接受了此地址设置包,会返回一个长度为0的数据包。主机接收到长度为0的状态包之后就会返回一个ACK给Device,Device再接收到这个ACK之后,就可以启用新地址了。这样Device就得到了一个唯一的设备地址,作为主机通信的唯一表示。
3、发送第三个Setup包,内容是80 06 00 02 00 00 09 00,这次是为了获取配置描述符集合的大小,此位位于读回数据的第三个字节。U盘返回的数据为09 02 20 00 01 01 00 80 32,即描述符集合总大小为0x20。
4、发送第四个Setup包,内容是80 06 00 02 00 00 09 00,和上次不同的仅仅是,这次要读回来的数据是整个配置描述符区域。U盘返回来的数据是09 02 20 00 01 01 00 80 32 09 04 00 00 02 08 06 50 00 07 05 82 02 40 00 00 07 05 02 02 40 00 00。这时候我们就可以知道该设备是什么类型的设备,支持什么样的操作了。
上述这两个过程也有的程序就是直接读取0xff个字符大小,当然同样可以达到读回设备描述符集合的目的。
至此,我们已经得到了所需要的设备信息,之后就可以对设备进行配置了。
5、向设备发送第五个Setup包,数据为00 09 01 00 00 00 00 00,USB Device返回一个长度为0的数据包,表明数据正确接收。至此,USB枚举过程就完成了。初始设置步骤:就是一个由SET事务构成的传输
可选数据步骤:就是一个由IN或OUT事务构成的传输,这个步骤是可选的,要看初始设置步骤有没有要求读/写数据(由SET事务的数据包阶段发送的标准请求命令决定)。
状态信息步骤:这个步骤就是要获取状态信息,由IN或OUT事务构成的传输,但是要注意这里的IN和OUT事务和之前的INT和OUT事务有两点不同:
1) 传输方向相反,通常IN表示设备往主机送数据,OUT表示主机往设备送数据;在这里,IN表示主机往设备送数据,而OUT表示设备往主机送数据,这是为了和可选数据步骤相结合;
2) 在这个步骤里,数据包阶段的数据包都是0长度的,即SYNC+PID+CRC16。