首页 > 其他分享 >浅谈 USB 枚举过程

浅谈 USB 枚举过程

时间:2023-12-19 21:22:17浏览次数:28  
标签:浅谈 主机 配置 枚举 描述符 USB 字节 设备

浅谈 USB 枚举过程


一、概述

  在我们的产品应用中,不管是鼠标、键盘、还是其他产品等等,有很多设备都离不开 USB 接口,我们不仅要清楚如何进行 USB 的硬件设计,也要懂得 USB 的具体协议规范,才能看懂对应的代码流程。那么下面我们就来了解下USB 的枚举流程。


二、USB 设备状态

  USB 枚举完成之前,USB 设备要经过一系列的状态变化,才能够最终完成枚举。
在 USB 设备接入主机到枚举完成的过程可分为以下的状态变化:连接状态、供电状态、默认状态、地址状态、配置状态、挂起状态。其中,当设备变成配置状态时,即可认为 USB 设备和 USB 主机间的枚举完成。首先,连接状态指的是 USB 设备需要先和主机建立起物理上的连接,USB 设备检测是否连接到主机以及 USB 主机检测是否有 USB 设备连接是 USB 协议能够进行后续工作的前提。简单说来,检测连接状态的必要条件就是在 USB 设备接入时某些状态发生了变化。USB 硬件和协议上可通过检测 VBUS 、D+、D- 这些信号线的电平信号状态来进行检测连接的方法,当然,USB 主机和设备也必须要具有检测断开的能力。关于检测连接的具体协议部分,大家感兴趣的可以去详读 USB 协议规范对应章节,在此不在赘述。

  1. 供电状态,USB 设备可以从 USB 的 VBUS 上获取电源,或者通过外部电源获取电源,这一种称为自供电设备。而我们比较常见的是通过 VBUS 供电的设备称为总线供电设备。
  2. 默认状态指的是 USB 设备进入供电状态后,在被复位之前,不能响应总线上的任何事务。只有当 USB 设备被复位,处于默认状态后,才会响应主机发送过来的请求。
  3. 地址状态,指的是 USB 设备在被复位后,且在 USB 主机给 USB 设备设置一个新的地址之前,所有的 USB 设备都使用默认的 0 地址与主机进行通信。当 USB 设备在收到主机发送的设置地址请求之后,USB 设备就会得到一个唯一的地址。USB 设备会一直保存这个地址并使用它与主机进行通信,直到设备被复位或者断开。即使 USB 设备在获取唯一地址之后进入挂起状态,设备依然会保留这个地址,并在总线恢复后继续使用该地址。在设备挂起期间,主机也不能把该唯一地址分配给其他的 USB 设备。
  4. 配置状态,指的是在 USB 设备功能能够使用之前,USB 设备和 USB 主机必须协商确定功能相关的配置项,所有的配置项都以描述符的形式提供,其中配置状态所需的描述符主要有以下 6 种:设备描述符、配置描述符、字符串描述符、接口描述符、端点描述符、设备限定描述符,当然,这里还有一种特殊的描述符称为接口关联描述符,用于将一组有关的描述符关联起来共同描述符一个特定的功能。USB 主机通过获取描述符请求来获得 USB 设备相关的描述符,通过设置接口和设置配置来设置设备的相关配置项。一旦相关功能参数被配置后,USB 设备就可以正常工作了。正常的 USB 设备在收到 USB 主机发出的复位信号后会进入默认状态,之前所指定的地址、所配置的接口和配置项都将无效,需要由 USB 主机重新指定设备地址,获取描述符并配置相关配置项,才能够使设备重新工作。
  5. 挂起状态,指的是 USB 设备在 USB 总线持续 3ms 没有活动时,设备就会自动进入挂起状态。有挂起则意味着有恢复状态,这两个状态切换时 USB 协议实现低功耗的一种机制。进入挂起状态后,USB 设备要维持所有的内部状态。

三、USB 枚举流程

  根据上面的状态变化过程,我们可以对 USB 协议进一步的了解。当 USB 主机检测到 USB 设备插入后,就要对设备进行枚举了。其实枚举的作用就是通过协议读取设备的相关信息,知道设备是什么样的设备,然后进行通信,这样主机端就可以根据这些信息安装合适的驱动程序了。USB 设备状态从初始的连接状态到最终的配置状态的变化过程就是整个枚举流程。枚举流程可分为以下几个步骤:获取设备描述符、设置设备的地址、获取完整设备描述符、获取配置描述符、获取字符串描述符、获取限定描述符、配置设备的配置描述符。

  1. 获取设备描述符,这是开始枚举的第一步,主机通过获取描述符请求来获取设备的描述符,对于高速设备和低速设备,用于控制传输的端点 0 支持的最大包长度是确定的,分别是 64 和 8 字节,但对于全速设备,端点0 支持的最大包长度可以是 8、16、32、64 字节,在 USB 主机获取到设备的设备描述符之前,USB 主机无法确定 USB 设备端点 0 的最大包长度,这样 USB 主机获取设备描述符的请求有可能不能正常完成。在双方通信前,USB 主机必须要指定端点0上的最大包长度,以便于正确检测控制传输种数据阶段的结束,假设 USB 主机端指定的 USB 端点 0 的最大包长度为 64 字节,而实际的 USB 设备端点 0 的最大包长度为 8 字节,由于 USB 设备描述符长度为固定的 18 字节,当主机发送第一个获取设备描述符请求时将会要求设备端发送 18 字节的数据,USB 设备在收到请求后,准备发送 18 字节的数据,但实际最大包长度为8字节,所以18字节需要分三个事务去发送完成,但主机端指定的最大包长度为 64 字节,当主机接收到第一个 8 字节的事务时,就认为数据阶段已经完成,而 USB 设备此时正在准备发送第二个 8 字节的事务,导致这个控制传输由于双方的通信状态不同步而无法正常完成。这也是复位后主机为什么需要再次获取设备描述符,也就是上面说到的针对一些 USB 设备,设备描述符可能需要分多次获取,这样有可能获取的描述符是不完整的,而复位后是要获取设备描述符的全部 18 字节。针对这个问题,不同 USB 主机的做法也有些不同,比如 Windows 7 系统在第一次获取设备描述符后,直接进行复位,将设备状态重置为初始状态。而由于设备端所支持的最小的最大包长度为8 ,此时主机一定能获取到设备描述符的前8个字节,而设备描述符的第8个字节就是设备端点0所支持的最大包长度,所以主机可以在后续的控制传输中使用这个设备端的最大包长度来保证双方正常通信。而 Ubuntu 的 USB 主机做法则有些不同,主机端第一次获取设备描述符只获取其前8字节,不管 USB 设备端点的最大包长度是多少,USB 设备只能发送8字节的数据,同时通信双方同步结束数据阶段。之后 USB 主机读取 USB 设备描述符的第8个字节,得到设备端点0的最大包长度,用于后续的控制传输。
  2. 设置设备的地址,主机发送设置地址请求来设置设备的地址,请求中的 Setup 数据内包含主机为设备分配的新地址,设备收到地址后,进入地址状态,在该设备未复位或断开之前,设备会一直使用该地址与主机通信,在本次传输中,USB 主机和设备都仍然使用地址 0 进行通信。在设备收到设置地址请求后,必须要在 50ms内处理该请求并完成该请求的状态阶段,在完成状态阶段后,可以有2ms 的恢复时间,在这个时间内 ,USB 主机不能使用其刚刚指定的地址与设备通信,在这个时间之后,设备必须要使用该新地址与主机进行通信,同时,设备不能对发送到旧地址的任何请求产生回复吗,除非新旧地址一致。
  3. 获取完整设备描述符,前面我们讲到了设备描述符部分,并且在上面的新地址设置完成后,主机与设备将会用新的地址通信,USB 主机会用新地址与设备进行通信,来获取完整的 18 字节的USB 设备描述符。USB 设备描述符的第18字节表示该设备配置描述符的数目。
  4. 获取配置描述符,由于每个设备配置描述符的长度都不一样,USB 主机无法提前得知设备的配置描述符长度,因此配置描述符的数据中必须要有表示长度的部分,由于第9字节代表该配置下 USB 设备的最大消耗电流值,因此主机将会先获取配置描述符的前9个字节,若该配置下的最大消耗电流值超过了USB 主机可提供的额度,该配置将不可用,如果设备中的所有配置都不可用,该设备也会不可用,这将会中止整个枚举流程。
  5. 获取字符串描述符,该部分主要包括了设备的一些字符串格式的信息,如设备制造商、产品信息和序列号、语言种类等。
  6. 获取限定描述符,USB 主机可能会尝试去获取设备的限定描述符,以获取USB 设备在其他速度模式下的信息。USB 设备也不一定会支持限定描述符,在这种情况下,USB 设备将会回复STALL 表示设备不支持该描述符,这种控制传输的失败并不影响设备的正常枚举和使用。
  7. 配置设备的配置描述符,主机在获取设备的某个配置描述符之后,会解析该描述符中所有的接口信息,和端点信息,之后主机就可以发出设置配置请求来通知 USB 设备使用哪一个配置。该请求命令中包含一个配置值,这个值是主机从设备的配置描述符中解析得来的,设备收到请求后,会判断配置值是否合法,若合法,设备就进入配置状态,设备会根据配置值选择对应的配置描述符,进行初始化操作,完成初始化后,设备就具备了与主机进行数据传输的能力,此时枚举阶段结束。以上则是整个枚举的过程,后续的 USB 通信将主要发生在配置描述符的接口或端点描述符所描述符的端点上。

四、总结

  通过上面的了解,我们就能够详细地熟悉整个 USB 的枚举过程了,每个部分的流程和细节我们都可以通过对 USB 协议规范去了解,也可通过画流程图的方式增加自己的理解记忆。


五、参考资料

【1】USB2.0.pdf
【2】微控制器USB 的信号和协议实现
【3】《device class definition for human interface device (HID)》.pdf

标签:浅谈,主机,配置,枚举,描述符,USB,字节,设备
From: https://www.cnblogs.com/VocalHero/p/17914777.html

相关文章

  • 【拜谢tgt】浅谈微积分在高中数学中的应用
    pdf版本(渲染较好)浅谈微积分在高中数学中的应用前言本文仅作为各类题型或技巧的归纳,以在高考中应用为目的。A\(\operatorname{L'H\hatopital's\;rule}\)不严格地说,洛必达法则就是在\(\frac{0}{0}\)型和\(\frac{\infty}{\infty}\)型时,有:\[\begin{aligned}\lim_{x\t......
  • spring boot 配置get方法枚举转换策略
    配置转换器@SuppressWarnings({"rawtypes","unchecked"})publicclassCompositeEnumConverterFactoryimplementsConverterFactory<String,Enum<?>>{ @Override public<TextendsEnum<?>>Converter<String,T>getC......
  • 普通非 root 用户挂载 USB 设备
    Root用户可以直接使用mount命令挂载硬盘(包括U盘),但是普通用户无法使用该命令。使用udisksctl挂载硬盘udisksctlmount-b/dev/sdx1#挂载udisksctlunmount-b/dev/sdx1#卸载udisksctlpower-off--block-device/dev/sdx1#断电udisksctl可能需要安装才能......
  • Maix II Dock 的USB OTG 及USB UART 测试
    1、通过USBOTG接口实现ADB的终端交互①、使用typeC数据线连接电脑和MaixIIDock板卡的USBOTG接口②、电脑弹窗并识别MaixIIDock板卡为一个“U盘”,如果提示U盘驱动有问题,请忽略。          ③、进入U盘可以看到对应的配置文件及一个app执......
  • 浅谈分布式事务
    事务:事务是指由一组操作组成的一个工作单元,这个工作单元具有原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。原子性:执行单元中的操作要么全部执行成功,要么全部失败。如果有一部分成功一部分失败那么成功的操作要全部回滚到执行前的状态。一致性:执行......
  • MySQL运维9-Mycat分库分表之枚举分片
    一、枚举分片通过在配置文件中配置可能的枚举值,指定数据分布到不同数据节点上,这种方式就是枚举分片规则,本规则适用于按照省份,性别,状态拆分数据等业务二、枚举分片案例枚举分片需求:现有tb_enum表,其中有id,username,status三个字段,其中status值为1,2,3当statu......
  • 枚举子集&高维前缀和学习笔记
    枚举子集首先\(n\)位二进制数可以表示一个大小为\(n\)的集合的所有子集。接下来的问题均用二进制数展开。一种暴力的想法是枚举所有数然后判一下是否满足条件,单次时间复杂度\(O(2^n)\),对所有数做一遍就是\(O(4^n)\)。发现有很多枚举是无用的,考虑怎么样让每次枚举出来的都......
  • 浅谈 Socket.D 与响应式编程
    一、Socket.D的主要特性首先,Scoket.D是高效一个二进制的网络通讯协议(官方我讲法是:基于事件和语义消息流的网络应用协议),能够满足很多场景下使用。其次,Scoket.D是温和的响应式(采用回调风格)。1、三种通讯模式send只是发送(发送后不管了)发送一个请求,无需为这个请求发送答复报......
  • 无涯教程-Java - Enumeration 枚举接口函数
    Enumeration接口定义了可以枚举对象集合中的元素的方法。下表总结了Enumeration声明的方法-Sr.No.Method&Remark1booleanhasMoreElements()当实现时,必须在提取更多元素时返回true,而在列举所有元素时返回false。2ObjectnextElement()这将返回枚举中的下一个对象......
  • 实验6 C语言结构体、枚举应用编程
    task11//P286例8.172//对教材上的程序作了微调整,把输出学生信息单独编写成一个函数模块3//打印不及格学生信息和所有学生信息程分别调用45#include<stdio.h>6#include<string.h>7#defineN3//运行程序输入测试时,可以把这个数组改......