上一篇:https://blog.csdn.net/qq_40088639/article/details/109752441
五、获取配置描述符集合(第一次)
通常来说,主机第一次先获取9字节长度的配置描述符,然后根据配置描述符中配置描述符集合的长度,再次获取配置描述符。第二次获取的时候,设备会将配置描述符、接口描述符、类特殊描述符、端点描述符等一并返回。这是一个比较合理的配置描述符请求过程。
但是,由于计算机系统的差异,有可能请求配置描述符的时候,直接请求255个字节的数据,然后设备会将配置描述符返回(返回的数据长度可以比Host实际请求的长度小)。比如,在开发xxx公司的芯片,主机是公司的电脑,win7操作系统,就有这种情况。这并不属于异常请求,并且不同的操作系统(win7/8/10、mac、android),请求配置描述符的长度和次数都不同,在实际开发过程中,要注意对比不同操作系统间的差异。
配置描述符也是标准描述符中的一种(五种标准描述符)。
注:这次获取配置描述符,使用的长度为0xFF。所以设备直接返回全部的配置描述符集合。 |
1. 配置描述符的结构
一个USB设置至少有一个配置描述符。标准配置描述符结构如下:
偏移量/字节 |
域 |
大小/字节 |
说明 |
0 |
bLength |
1 |
该描述符的长度(9 Byte=0x09) |
1 |
bDescriptorType |
1 |
该描述符的类型(配置描述符是0x02) |
2 |
wTotalLength |
2 |
配置描述符集合 的总长度 |
4 |
bNumInterfaces |
1 |
该配置下的接口数 |
5 |
bConfigurationValue |
1 |
该配置的值 |
6 |
iConfiguration |
1 |
描述该配置的字符串的索引值 |
7 |
bmAttributes |
1 |
该设备的属性 |
8 |
bMaxPower |
1 |
该设备所需的电流(单位 2mA) |
说明:
(1) wTotalLength是整个配置描述符集合的总长度,配置描述符集合就包括:配置描述符自身的长度、接口描述符、类特殊描述符、端点描述符。
(2) bNumInterfaces是该设备所支持的接口数量。通常来说,功能单一的设备就只有一个接口(比如鼠标、键盘),而组合设备则具有多个接口(比如音频设备[ HID + UAC ])。
(3) bConfigurationValue:一个USB设备可以有很多个配置。bConfigurationValue就是每个配置的标识!接下来会讲到设置配置这个标准请求,进行设置配置这个请求的时候,主机会发送一个配置值,如果某个配置的bConfigurationValue和主机请求的配置值相匹配,就表示该配置被**,USB设备就使用这个配置。(由主机决定,设备使用哪个配置)
(4) iConfiguration为0,则表示没有字符串来描述该配置描述符,这个字段一般都设置为0。
(5) bmAttributes:大小为一字节,不同的位,表示不同的特性,如下。
1) 第7位D7是保留的,必须为1。 2) 第6位D6表示供电方式:1设备自供电;0设备是总线供电的。 3) 第5位D5表示是否支持远程唤醒:1 支持远程唤醒 0 不支持远程唤醒 4) 第0位到第4位D4~D0是保留的,默认为0。 |
(6) bMaxPower:大小为1字节。表示设备从总线获取的最大电流量,单位是2mA。比如,如果需要200mA的电流,那么该字节的值就是100。
2. 配置描述符在程序中的定义
配置描述符和设备描述符是定义在一起的,而且是标准定义方式。因为配置描述符集合里面的子集是要根据实际的产品开发进行配置的,所以,还需要进行修正。分析下面的原生代码,在跳转到usb_fix_descriptor()函数检查分析和修正配置描述符集合的总长(动态计算),结合协议分析仪的数据,即可理解,当前配置描述符集合里面有多少个子集。
usb_fix_descriptor()函数:功能就是构造配置描述符集合(分配各个指针的指向)。定义如下:
配置描述符集合,一般由配置描述符、接口描述符、端点描述符、HID描述符、报告描述符等来构成,这些描述符在程序中都有定义。
这里要注意配置描述符 和 配置描述符集合是两个不同的概念。
一个设备只有一个设备描述符,一个设备描述符可以有多个配置,每个配置都抽象用一个配置描述符来表示,一个配置又有多个接口,一个接口又可以有多个端点(设备各个描述符之间的关系)。
3. 接口描述符的结构
接口描述符不能单独返回,要附着配置描述符一并返回(设备返回配置描述符集合)。
偏移量/字节 |
域 |
大小/字节 |
说明 |
0 |
bLength |
1 |
描述符的长度(9 Byte) |
1 |
bDescriptorType |
1 |
该描述符的类型(0x04) |
2 |
bInterfaceNumber |
1 |
该接口的编号(从0开始) |
3 |
bAlternateSetting |
1 |
该接口的备用编号 |
4 |
bNumEndpoints |
1 |
该接口所使用的端点数 |
5 |
bInterfaceClass |
1 |
该接口所使用的的类 |
6 |
bInterfaceSubClass |
1 |
该接口所使用的的子类 |
7 |
bInterfaceProtocol |
1 |
该接口所使用的的协议 |
8 |
iInterface |
1 |
描述该接口的字符串的索引值 |
说明:
(1) bInterfaceNumber:当一个配置有多个接口时,每个接口的编号都不相同,从0开始递增对一个配置的接口进行编号。这里需要注意,对照一下,配置描述符里面,支持多少个接口(这个域:bNumInterfaces)。
(2) bAlternateSetting:备用端口号,也是从0开始。一般很少用到该字段,设置为0即可。Alternate:备用,但是在开发组合设备的时候,经常用到,比如开发UAC类的组合设备。
(3) bNumEndpoints:是该接口使用的非0端点数(不包括0端点,因为端点0是每个设备必须有的硬件端点,枚举过程的控制传输需要端点0)。如果该字段设置为0,那么表示没有非0端点。
(4) bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol:分别是接口所使用的类、子类、和协议,对应的代码(编号)由USB协议来定义。跟设备描述符中的意义很相像,设备描述符也有类似的三个域。
(5) iInterface:如果设置为0,则表示没有字符串。
4. 接口描述符在程序中的定义
注意这里的数据和协议分析仪里数据的对应验证。USB协议分析仪捕获到的是9个字节的数据(单独从配置集合里面提取出这部分):09 04 00 00 02 03 00 00 00
仔细分析这个请求,可以发现Host并没有按照常规,先请求9个字节的配置描述符,然后再请求配置描述符集合,而是,直接请求255个字节的配置描述符集合数据。那么可以发现,设备分三次返回集合数据。因为端点0允许的最大数据包长是64字节,所以,130字节(该实例的配置描述符集合总长)的集合数据分三次发送。130=64+64+2
这130字节的数据就包括了配置描述符、接口描述符、端点描述符等子集数据。在程序中都有定义。
5. 端点描述符的结构
端点描述符不能单独返回,要附着配置描述符后一并返回。
偏移量/字节 |
域 |
大小/字节 |
说明 |
0 |
bLength |
1 |
该描述符的长度(7 Byte) |
1 |
bDescriptorType |
1 |
该描述符的类型(0x05) |
2 |
bEndpointAddress |
1 |
该端点的地址及输入输出属性 |
3 |
bmAttributes |
1 |
该端点的传输类型属性 |
4 |
wMaxPacketSize |
2 |
该端点支持的最大包长 |
6 |
bInterval |
1 |
端点的查询时间 |
说明:
(1) bEndpointAddress:大小1字节,端点的地址。
最高位,也就是第7位D7:表示该端点的传输方向。1表示输入(像Input的第一个字母) 0表示输出(像Output的第一个字母) D6~D4:保留位,默认为0。 D3~D0:才是端点号 |
(2) bmAttributes是端点的属性。
1)最低两位D1~D0:表示该端点的传输类型。0为控制传输;1为等时传输;2为批量传输;3为中断传输。两个位,也就四种情况:00 01 10 11 2)如果是非等时传输(控制传输、批量传输、中断传输中的一种),那么D7~D2都为0 3)如果该端点是时等时传输,则: D3~D2表示同步的类型:0为无同步、1为异步、2为适配、3为同步。 D5~D4表示用途:0为数据端点、1为反馈端点、2为暗含反馈的数据端点、3是保留值。 D7~D6:保留,设为0。 |
(3) wMaxPacketSize:大小2字节,表示该端点支持的最大包长。对于低速和全速设备,D10~D0表示端点的最大包长,其他位保留为0。对于高速模式,D12~D11是每个数据帧附加的传输次数,具体查看USB2.0协议。
(4) bInterval:表示端点查询的时间。对于中断端点,表示查询的帧间隔数,对于等时传输和高速模式的中断、批量传输,该字段的意义,查看USB2.0协议。
6. 端点描述符在程序中的定义
从代码中可以知道,定义了两个端点描述符,USB协议分析仪捕获到的是两个7个字节的数据(单独从配置集合里面提取出这部分)
07 05 81 03 40 00 01 07 05 01 03 40 00 01 |
协议分析仪里面的数据:
7. HID描述符的结构
之所以有HID描述符,是因为前面说过,这个实例是一个组合设备(UAC+HID),USB鼠标是属于USB HID类的,那么对于HID类的设备,在配置描述符集合中,还需要有一个HID描述符子集。HID描述符是一个类描述符,应该跟在接口描述符后面。
HID描述符的结构
偏移量/字节 |
域 |
大小/字节 |
说明 |
0 |
bLength |
1 |
该描述符的长度 |
1 |
bDescriptorType |
1 |
该描述符的类型(HID 描述符为0x21) |
2 |
bcdHID |
2 |
HID协议的版本 |
4 |
bCountryCode |
1 |
国家代码 |
5 |
bNumDescriptors |
1 |
下级描述符的数量 |
6 |
bDescriptorType |
1 |
下级描述符的类型 |
7 |
wDescriptorLength |
2 |
下级描述符的长度 |
9 |
bDescriptorType |
1 |
|
10 |
wDescriptorLength |
2 |
|
|
…… |
… |
|
说明:
(1) bLength是描述符的总长度。总长度和下级描述符的个数有关,比如只有一个下级描述符的时候,bLength=1+1+2+1+1+1+2=9字节(0x09)
(2) bcdHID是设备使用的HID协议版本号,比如USB HID1.1就是0x0101(BCD码、小端模式)。
(3) bCountryCode是设备所适用的国家。通常使用的键盘是美式键盘,代码为33,十六进制数就是0x21。
(4) bNumDescriptors是下级描述符的数量,该值至少为1。也就是说,至少要有一个报告描述符(下文会讲到,什么是报告描述符,在单独介绍HID类设备的时候,也会有详细的解析)!下级描述符可以是报告描述符或者是物理描述符。
(5) bDescriptorType是下级描述符的类型,假如下级描述符是报告描述符,则编号为0x22;物理描述符的编号为0x23。
(6) wDescriptorLength:下级描述符的长度,大小用两字节来表示。
8. HID描述符在程序中的定义
协议分析仪中的数据:09 21 10 01 00 01 00 11 01
串口打印跟踪:
HID描述符的下级描述符是报告描述符,长度是0x0111=273 Byte。什么是报告描述符?长度为什么是273?Host请求报告描述符的过程是怎样的接下来的博文中会有分析。
上面的分析的是win7系统下主机的请求,也就是说,主机直接请求255字节长度的配置描述符集合数据。之后,设备就直接返回配置描述符集合的所有数据。
9 一个正常的配置描述符请求过程
主机获取配置描述符集合,比较正常的请求过程应该是这样的:第一次先获取9字节长度的配置描述符,然后根据配置描述符中配置集合的长度,再次获取配置描述符集合。第二次获取的时候,设备会将配置描述符、接口描述符、类特殊描述符、端点描述符等一并返回。这是一个比较合理的配置描述符请求过程
串口打印跟踪:
下一篇:https://blog.csdn.net/qq_40088639/article/details/109768241