前面学习了那么多的概念,这里需要记住一点分层概念即设备 ---> 配置 ---> 接口 ---> 端点,这种分层的概念结构 。 也可以理解为端点构成接口,接口组成配置,配置组成设备。
USB设备的枚举过程
前面说过了,USB只是一种串行通信总线。也就是说USB是一种物理通信通道。但是实际中我们使用了各种各样的USB设备,如U盘,鼠标、键盘等。这些设备都是使用USB总线和主机(PC)进行通信的,但是主机却可以区分不同的设备,这里靠的就是,前面定义的描述符。当我们将USB设备接入主机后主机很快就能知道设备的属性并为其安装对应的驱动,其实在这之前,设备已经和主机进行了很多次数据交换了,这个过程就是枚举。开始了解枚举之前还有明白一个规则,每个USB设备都至少有一个端点即0,这个端点就是USB的控制端点,用来USB设备自己和主机主机间“协商”交换数据用。如端点地址 0x00表示控制输出端点地址,0x80表示控制输入地址。接下来看枚举过程:
- 首先当主机检测到设备插入后就会将USB设备复位,复位后的USB设备默认地址就是0,这样主机就可以用地址0作为设备地址进行通信了。
- 然后主机会向端点0发送获取设备描述符的建立过程,然后设备将在数据过程将设备描述符返回给主机,这里注意如果端点0的最大包长小于设备描述符,设备本应该分多次数据过程,但是主机只会读取第一次的数据,因此设备实际这个过程只需要一次数据过程即使一次无法将设备描述符全部传输。同时USB标准还规定端点0的最小包长至少是8,因为设备描述符的第八个字节就包含了端点0的最大包长,主机会根据这里的值,获取设备的端点0的最大包长。
- 然后主机会再一次对设备复位,然后发送给设备地址0一个设置地址的命令,地址包含在设置设备的建立过程中的数据包中。注意这个地址是由主机管理的(1~127)。设备收到这个命令后直接到状态过程,并在主机请求状态时返回一个长度为0 的状态给主机,如果主机回应了ACK给设备,设备在此之后就将启用这个地址。
- 然后主机使用新地址再次获取设备描述符,这次主机会多次读取设备描述符数据,直到读取完全部的设备描述符。
- 主机再获取配置描述符(设备返回配置描述符时需要将 配置描述符、接口描述符,类特殊描述符、端点描述符一起返回回去,而不能单独返回)。
- 然后是获取报告描述符(如果是HID设备会有这一步)。
- 这一部分不是必然的,设备会根据前面描述符提供的字符串索引获取字符串描述符,从而方便人们阅读设备的相关信息。
以上的这些过程都是使用的USB控制传输。
以上过程忽略了一个概念,就是USB标准请求,即获取描述符是怎么告诉设备的,也有一套标准通信规则如下表:
bRequest的命令码表
具体的请求命令帧组成,可以查阅USB标准文档。这里拿一个不完整的设备枚举过程展示一下设备的枚举过程,数据使用BusHound 软件抓包得到的。
//获取设备描述符 CTL 80 06 00 01 00 00 12 00 //设备返回设备描述符 IN 12 01 00 02 00 00 00 40 93 39 16 14 00 02 01 02 03 01 //先获取设备配置描述符的前9个字节 == 获取知配置描述符的总长度 CTL 80 06 00 02 00 00 09 00 //设备返回配置描述符的前9个字节 == 得知配置描述符的总长度为0x29(41个字节) IN 09 02 29 00 01 01 00 c0 64 //获取全部配置描述符 CTL 80 06 00 02 00 00 29 00 //设备返回全部配置描述符其中还包括接口配置描述符和断点配置描述符(因为是HID设备还有一个HID设备配置描述符) IN 09 02 29 00 01 01 00 c0 64 09 04 00 00 02 03 00 00 00 09 21 10 01 00 01 22 21 00 07 05 81 03 40 00 01 07 05 01 03 40 00 01 //设置配置值,配置描述符中有一个位置就是配置这个值的 CTL 00 09 01 00 00 00 00 00 //获取接口 CTL 21 0a 00 00 00 00 00 00 //获取HID设备配置描述符 CTL 81 06 00 22 00 00 61 00 //设备返回全部HID设备报告配置描述符 IN 05 8c 09 01 a1 01 09 03 15 00 26 00 ff 75 08 95 40 81 02 09 04 15 00 26 00 ff 75 08 95 40 91 02 c0
上面大概说了一下设备的枚举过程,如果以上过程完成了即---在电脑中可以看到一个USB设备了,接下来就是完成对应的用户数据传送过程的应用逻辑代码的实现了,这个根据不同的设备包括自定义设备,这一部分除非是一些标准设备,否则你可以做的很有个性化,只要收发双方约定一个统一的标准规则就可以。但是需要记住的是如果你要使用USB进行数据的收发,配置描述符中应该包含一个非0端点的配置,因为端点0是仅用于控制传输的。具体的实现可以详细参考《圈圈教你玩USB(第二版)》一书。
参考:《圈圈教你玩USB(第二版)》
22:17:11 2019-06-04