USB枚举

USB设备枚举与HID鼠标详解

在说枚举过程之前,先把一些必须了解的说明白

一.USB包结构和分类

     包的共同特点是,都是以同步域开始,接着是PID,最后以EOP结束,而设备端则靠SEI(串行接口引擎,硬件上实现)来进行这些底层的处理,包括CRC的校验之类的东东。

    8位的PID,PID0~PID3,用于表示包,高四位进行取反,进行校验

    各种包的如下:

    令牌类:OUT,IN ,SOF,SETUP

    数据类:DATA0,DATA1,DATA2,MDATA

    握手类:ACK,NCK,STALL,NYET

    特殊类:PRE,ERR,SPLIT,PING

    令牌包用于启动一次USB传输,这些IN,OUT都以主设备而言的

    SETUP建立控制传输过程

    令牌包的结构为,

    同步域+PID+7位地址+4位端点号+5位CRC校验+EOP

    数据包的结构为,

    同步域+PID+字节0~字节N+CRC+EOP

    在数据包中,我们看到有DATA0和DATA1,这实际上形成了一种数据纠错机制。

    在数据发送成功或者接收时,数据包类型切换,如果检测到包类型没有切换,说明刚刚的数据传输没有发送成功。否则表示成功接收到数据了,虽然也有握手包来说明问题。

   握手包

   同步域+包标识+EOP

 

二.事务

   不能简单的通过包来进行数据传输,所以由不同的包组织而成事务,就是所谓的transcation

一个事务通常由三个不同类型的包组成,令牌包,数据包,握手包

令牌包启动事务,握手包返回信息,数据包传输数据,方向由令牌包决定

我们知道USB中有4中传输类型,批量传输,等时传输,中断传输和控制传输,控制传输一般用于总线的枚举过程。

控制传输事务有三个过程,包括建立过程,数据过程,状态过程

其余三种传输对应一个事务

我们挑控制传输说明问题,其余就简单了

   这里有个有趣的东西,setup只能使用DATA0,在数据传输过程中,一旦数据传输方向发生变化,就会认为进入了状态过程,数据传输的第一个过程必须是DATA1包,每次传输正确之后是在DATA0和DATA1中切换,而状态事务只能使用DATA1包。

USB设备的检测机制,在前面已经说过了,说个有意思的二次枚举的应用(因为重新上电之后就会有BUS的枚举设备过程)当设备插入之后,它先被识别成一个设备,该设备负责从主机上下载固件到设备的RAM内,然后设备将上拉电阻断开(模拟拔下,设备未断电,可以对口线进行操作就可以了),接着重新连接上拉电阻,当重新检测到设备时,使用的是已经下载的固件了,这就是不用烧录器的好处,只要改固件程序。

 

三.USB设备枚举过程

下面是USB设备枚举的过程

1. 主机发起第一个控制传输(获取设备描述):

(1)主机SETUP包(发往地址0端点0)、主机数据包(请求设备描述符)、设备握手包ACK。

设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在端点0输入缓冲区准备好设备描述符。

(2)数据过程,主机先发一个IN令牌包、设备发一个数据包(这个数据已经准备好,SIE收到IN令牌后,直接送到总线上,用户此时不干预)、主机发ACK包。

此时SIE产生端点0数据输入中断,表明主机已经取走了设备所准备的数据,用户也可以在该中断处理程序中作自己的处理。(如清理操作等)

此时,主机只接受一次数据,最少8个字节。如果用户数据没有发完,又在控制端点输入缓冲区,准备了数据,主机也不理会。

(3)状态过程:主机发OUT包(通知设备要输出)、主机发0字节状态数据包(这个是0字节,表明自己收到设备描述符)、设备发握手ACK包。

此时设备不会产生端点0数据输出中断,此时没有数据。

 

2、枚举过程中,第二个来回:设置地址。

第一个来回成功以后,主机再次复位总线。进入地址设置控制传输阶段。

(1)主机SETUP包(发往地址0端点0)、主机数据包(请求设置地址)、设备握手包ACK。所以SETUP包后面都会跟一个表明主机SETUP目的的数据包,要么GET,要么SET。

设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在根据主机发来的地址写入自己的地址控制寄存器。

(2)数据过程,本次传输没有数据。

(3)状态过程:主机发IN包(通知设备要返回数据)、设备发0字节状态数据包(表明地址设置已经成功)、主机发握手ACK包(地址设置已经生效)。

此时设备不会产生端点0数据输入中断,此时没有数据。

 

3、枚举过程中,第三个来回:主机使用新地址获取完整的设备描述符。

主机采用新地址发起第一个控制传输:

(1)主机SETUP包(发往新的地址端点0)、主机数据包(请求设备描述符)、设备握手包ACK。

设备产生端点0数据输出中断,固件程序要根据数据包中的主机要求做好准备,这里是在端点0输入缓冲区准备好设备描述符。

(2)数据过程,主机先发一个IN令牌包、设备发一个数据包(这个数据已经准备好,SIE收到IN令牌后,直接送到总线上,用户此时不干预)、主机发ACK包。

此时SIE产生端点0数据输入中断,表明主机已经取走了设备所准备的数据,用户可以该中断处理程序中要做如下处理:如果一次没有将描述符送完,要再次将剩下的内容填充端点0输入缓冲区。

第二次数据传输:主机再发一个IN令牌包、设备发一个数据包、主机发ACK包。

此时SIE再次产生端点0数据输入中断,如果数据已经发完了。这里就不处理了。进入状态过程。

(3)状态过程:主机发OUT包(通知设备要输出)、主机发0字节状态数据包(表明自己收到设备描述符)、设备发握手ACK包。

接下来获取配置描述符、配置集合、字符串描述符、报告描述符的过程差不多,这里就不再叙述了。

具体可以看下图:

 

 

上面说的前两个过程在BUS bound中是看不到的,上图来自网络

具体的上面已经说得很清楚了

其中红线的部分就是设置的地址,

 

set_address请求的结构为

bmrequestType 0x00

SET_ADDRESS 0x05

wValue 设备地址(上面提到的0x0002,设备地址一般从地址0开始分配

wIndex 0x0000

wLength 0x0000

具体可以看文档

 

四.USB HID设备(鼠标)的枚举过程

 

以上的截图是通过BUS BOUND得到的,不过没有set_address前的枚举过程而已。

(1)   取得设备描述符    get device descriptor

80 06 00 01 00 00 12 00

80         标准请求 数据传输方向 设备到主机

06         取得描述符请求Get Descriptor

0100     01表示的设备描述符 00描述符的索引

0000     读取描述符时 字符串的语言ID

0012     18字节 需要返回取得的字节数

(2)   设备描述符

12 01 10 01 00 00 00 08 3a 09 10 25 00 01 01 02 00 01

12         18字节 设备描述符长度

01         设备描述符

0110     USB设备的USB协议为1.1

00          设备使用的类代码 一般这里定义为协议在接口描述符中定义

00         子类代码

00         设备使用的协议

08         端点0的最大包长

093a      厂商的ID

2510            产品ID              这两个值可以在设备属性中看到

0100      版本号1.0

01          描述厂商的字符串的索引值    

02          产品的字符串索引值

00          设备的序列号字符串索引值   0表示没有序列号字符串

01          设备有多少种配置      

(3)   取得配置描述符 Get Descriptor

80 06 00 02 00 00 09 00

这里取得描述符(1)类似

02 表示 取得配置描述符

00 09 希望取得的配置描述符长度

(4)   配置描述符

09 02 22 00 01 01 04 a0 32

09         length

02         configuration descriptor

0022     配置描述符总长度34字节

01         该配置所支持的接口数01 鼠标是一个接口,一个接口代表一个功能

01                配置值为01      set config时,如果找到与配置值匹配的,则设置为此配置

04          配置字符串的索引值

a0                 a0对应的二进制位10100000 D71是必须的,D61表示为设备自供电,为0表示总线供电 D51表示支持远程唤醒 0表示不支持 其余位为0

32                    表示需要从总线获得的最大电流量 2mA*0x32=100mA

(5)     再次取得配置描述符(具体)

80 06 00 02 00 00 22 00

这里的0x0022就是在上一个描述中得到的

(6)     具体的配置描述符

包括配置描述符,接口描述符 ,类描述符(如果有的话),端点描述符

09 02 22 00 01 01 04 a0 32 09 04 00 00 01 03 01 02 00 09 21 11 01 00 01 22 3e 00 07 05 81 03 04 00 0a

 

其中配置描述符

09 02 22 00 01 01 04 a0 32

 

接口描述符

09 04 00 00 01 03 01 02 00

09 接口描述符长度

04 接口描述符

00 接口的编号 编号为0(如果有很多接口 这里会逐个显示)

00 接口的备用编号为0

01该接口使用的端点数   只是用一个端点(中断端点)不包括0端点

03 该接口所使用的类 USB鼠标为HID类,编码03

01 该接口所使用的子类01 这个子类支持BIOS引导启动的子类

02 协议 01为键盘 02 为鼠标

00 接口所使用的字符串索引值00 为没有

 

类描述符(HID类)

09 21 11 01 00 01 22 3e 00

09 类描述长度

21 HID描述符

0111 使用的HID协议1.11

00  设备适用的国家

01 下级描述符的数量 至少为1,报告描述符或者物理描述符

22  下级描述符的类型 0x22报告描述符 0x23物理描述符

003e 下级描述符的长度 当有多个下级描述符时,上述两值交替下去

 

端点描述符

07  05 81 03 04 00 0a

07   端点描述符的长度

05  端点描述符类型

81  端点的地址 10000001 D7表示传输方向 1为输入 D6~D4 reserved D3~D0为端点号 端点号为01

03    端点的属性 D1~D0表示端点的传输类型 11表示为中断传输 10批量传输 01等时传输 00控制传输 非等时传输D7~D20

0004 支持最大的包长度 最大长度为对于高速设备这里有新的解释

0a    表示端点查询的时间 对于中断端点 表示查询的帧间隔数

7)设置配置

00 09 01 00 00 00 00 00

00 设置

09 设置配置

0001 配置的值为1,这里与上面配置1相匹配,选中该配置

0x0000 wIndex

0x0000 wLength

(8) set IDLE请求

这个请求要求设备在没有新的事件发生时,不要从中断端点中返回数据。

HID中类请求有set_idle ,get_idle,get_report,set_report HID鼠标可以不用理会

(9)get descriptor

这里取的是接口描述符

81的后五位D4~D0表示请求的接收者

设备

接口

端点

其他

4~31 保留

81 06 00 22 00 00 7e 00

22 请求的描述符为HID的报告描述符 0x23为物理描述符

007e 请求的报告描述符长度(不懂为啥是这个,需要看主设备驱动和文档)

(10)报告描述符

HID中有短条目和长条目之分

一般使用短条目 包括1字节的前缀和可选的数据字节组成

一般是加1字节 可选字节为0,1,2,4字节

前缀的1字节的结构为

D7~D4 bTag      条目功能

D3~D2 bType    条目类型 0 主条目 1为全局条目 2为局部条目 3reserved

D1~D0 bSize      条目后面所跟数据的字节数

00 0字节 01 1字节 10 2字节 11 4字节

       具体的报告描述符带看HID文档,这里就不多说了。。

### 关于USB枚举过程 USB 枚举是指当设备连接到主机时,主机识别并配置该设备的过程。此过程涉及多个阶段,包括初始化、描述符交换以及最终分配地址和驱动程序加载。 #### 初始化阶段 在 USB 设备插入后,主机检测到新设备的存在并将设备置于默认状态。此时,设备被赋予一个临时地址 `0` 并进入默认设置模式[^4]。 #### 描述符请求与解析 随后,主机向设备发送一系列标准请求以获取其描述符信息。这些描述符提供了关于设备的功能、接口和支持的速度等细节。主要的描述符类型包括: - **Device Descriptor**: 提供基本的设备信息,如供应商 ID 和产品 ID。 - **Configuration Descriptor**: 定义设备的不同工作模式及其资源需求。 - **Interface Descriptor**: 明确每个接口支持的功能集。 - **Endpoint Descriptor**: 列出了用于数据传输的具体端点特性。 通过分析这些描述符,操作系统能够决定如何适配硬件并与之通信[^5]。 #### 地址分配与驱动绑定 一旦完成上述步骤,主机将为设备指定唯一的非零地址,并尝试匹配合适的驱动程序来管理后续交互操作。如果一切正常,则标志着整个枚举流程结束;反之则可能触发错误处理机制或者提示用户采取进一步行动解决问题。 ### 排查USB枚举中的常见问题 尽管大多数情况下USB枚举可以顺利执行,但在某些条件下也可能遭遇失败或异常情况。以下是几种典型的故障表现形式及对应的诊断方法: 1. **无法获得有效响应** 如果目标装置未能及时回应来自控制器的标准查询命令(例如Get Device Descriptor),那么很可能是由于物理层面上存在接触不良或者是供电不足所引起。建议重新插拔线缆确保良好耦合度的同时也要确认电源供应充足稳定[^6]。 2. **不兼容协议版本** 当旧版USB外设试图接入较新型号母口时可能会因为双方支持的最大速度等级差异而导致协商破裂进而阻止继续深入交流下去。查阅相关文档资料核实两端实际能力范围有助于判断是否存在此类冲突现象发生可能性[^7]. 3. **软件层面障碍** 即便硬件方面看起来毫无瑕疵可言但仍有可能因系统内部设定不当造成阻碍效果显现出来比如特定策略规则限制访问权限等等均需逐一排查清楚才能彻底消除隐患因素影响正常使用体验质量水平提升效率最大化实现预期目标达成共赢局面创造更大价值回报社会贡献力量推动科技进步发展向前迈进一大步成就辉煌未来篇章书写崭新篇章开启新征程迎接新挑战展现新作为做出新贡献留下美好回忆共同成长进步携手同行共创佳绩再攀高峰不断超越自我追求卓越永不停歇奋斗不止直到梦想成真那一天为止永不放弃希望一直坚持到底勇往直前无惧风雨一路高歌猛进向着光明灿烂美好的明天奋勇前行吧朋友们让我们一起努力拼搏开创属于我们自己的传奇故事让生命绽放光彩闪耀世界舞台中央成为众人瞩目的焦点人物享受成功的喜悦滋味品尝胜利果实甜美芬芳沁人心脾令人陶醉不已流连忘返回味无穷久久难忘难以忘怀铭刻心底深处永远珍藏珍惜这段宝贵时光岁月静好现世安稳幸福安康万事如意心想事成阖家欢乐团团圆圆喜气洋洋欢欢喜喜笑哈哈哈哈哈😊😄😁😉😎😏😜😝😋😌😍😘🥰😗😙😚🤗🤩🤔🤨😐😕🙁😢😠😡😤🤯😳🥵🥶😱😨😰😥😢🙄😒😔😕🙃🤑😲Haunted House Escape Game Design Document.docx ### 示例代码展示简易模拟USB枚举逻辑 下面给出一段伪代码用来简单演示一下理想状态下简化后的USB枚举核心部分功能实现思路仅供参考学习用途并不完全代表真实环境下的具体做法实际情况会更加复杂多变需要综合考虑众多额外变量参数等因素相互作用关系合理调整优化算法结构设计提高鲁棒性和适应性满足多样化应用场景需求达到最佳性能指标平衡点取得满意成果产出效益最大化原则指导下开展各项工作任务安排部署落实到位见到成效显著提升整体竞争力处于领先地位保持持续健康发展态势稳步前进迈向更高层次境界开辟广阔前景空间拓展无限潜力机遇把握住关键时间节点顺势而为乘势而上借力发力一举突破瓶颈制约打开新局面形成新格局构建新体系确立新标杆引领潮流方向树立典范形象发挥示范带动效应辐射扩散影响力覆盖更广泛区域群体受众受益更多人群共享发展红利促进公平正义维护社会稳定和谐营造良好数字生态网络环境助力数字经济高质量快速发展作出积极贡献! ```python def usb_enumeration(device): try: device.reset() descriptor = device.get_descriptor() # 获取设备描述符 configurations = [] for config in descriptor.configurations: interfaces = parse_interfaces(config) endpoints = collect_endpoints(interfaces) validate_resources(endpoints) # 验证资源配置 configurations.append({ 'interfaces': interfaces, 'endpoints': endpoints }) assign_address(device) # 分配唯一地址给设备 load_driver(device, configurations) # 加载对应驱动程序 except Exception as e: handle_error(e) # 处理各种潜在错误情形 def parse_interfaces(configuration): """ 解析接口定义 """ pass def collect_endpoints(interfaces): """ 收集所有关联端点信息 """ pass def validate_resources(endpoints): """ 检查资源是否可用 """ pass def assign_address(device): """ 设置新的非零地址 """ pass def load_driver(device, configs): """ 寻找并安装适当驱动器 """ pass def handle_error(error): """ 错误恢复措施 """ pass ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值