USB HID Enumeration USB_GET_PROTOCOL

本文探讨了USB鍵盤在新PC平台上无法通过枚舉的问题,并通过USB分析仪发现,问题出现在USB_GET_PROTOCOL请求处理不当。文中详细解释了BOOT PROTOCOL和REPORT PROTOCOL的区别及其应用场景。

今天在一個新PC平臺上測試USB鍵盤,發現一直枚舉不通過。試想在以前的PC平臺上沒有任何問題為什么這次卻不通過了呢?

使用USB分析儀發現問題出現在USB_GET_PROTOCOL上,以前都是對USB_SET_PROTOCOL進行處理,而USB_GET_PROTOCOL沒用處理過,這次剛好遇到!

原來在USB HID Enumeration期間,PC會詢問設備的PROTOCOL,分為BOOT PROTOCOL  (0X00)和REPORT PROTOCOL (0X01),我再進入BIOS時,在USB_GET_PROTOCOL回復為0X01,在USB_SET_PROTOCOL里面PC設置為0X00,詳情可見下面兩張圖片:

 

這下大家明白了吧:

如果還不明白,可以參考如下網址,對PROTOCOL的詳細介紹

USB HID class: Difference between report protocol and boot protocol?(设计USB键盘进入BIOS会用到)

http://www.eefocus.com/sbogwxf230/blog/2012-05/204299_6313c.html

原文如下:

For USB-HID class: What is the difference between report protocol and boot protocol?

In short, boot protocol is used on BIOS, report protocol is used on OS.

The device capability of boot protocol is shown at the interface triad,
(interfaceClass, interfaceSubclass, interfaceProtocol) field on the HID interface descriptor.
(interfaceClass, interfaceSubclass, interfaceProtocol) = (3, 1, 1): boot keyboard
(interfaceClass, interfaceSubclass, interfaceProtocol) = (3, 1, 2): boot mouse

BIOS checks just this triad, and it recognizes the device (interface) as specified.
BIOS doesn't actually read out report descriptor from the device; It assumes that the device has "standard" keyboard or mouse report descriptor (*1) while the device in boot protocol.
After enumeration, BIOS puts Set_Protocol( BOOT ) to switch the device into boot protocol, if the device has boot capability. While BIOS is running, the device works as keyboard or mouse.
If the device doesn't have boot capability, BIOS doesn't enumerate the device.

At the start up of OS after BIOS, OS puts bus reset. The device gets back to default report protocol.
Usually, OS doesn't put any Set_Protocol, the device is held in report protocol.
On the enumeration, OS reads out report descriptor of the device, and it determines the type of HID device, regardless of above subclass-protocol field.

(*1) see HID spec Appendix B: Boot Interface Descriptors


>2- For keyboard: the input report format is unique for both report/boot protocol?

As I wrote above,
For boot protocol, the report format is fixed one.
For report protocol, you can define any report format on the report descriptor.


>3- When Host send Request(GET_DESCRIPTOR) specifying the report descriptor type, Will Device return the report descriptor including the input report data(i.e: 8-byte for keyboard) ? OR Host need to send Request GET_REPORT to get the input report?

Device returns just the report descriptor, when host puts Get_Descriptor().
Actual input report is sent for Get_Report( input ) request, or for IN transfer over the interrupt IN endpoint.

 

<think>好的,现在我要处理用户的问题:“usb_hid.cur_status一直是0”。首先,我需要理解这个问题的背景。用户提到的是USB HID设备的当前状态变量cur_status一直为0,这可能意味着设备未能进入预期的状态,比如配置完成状态(USBD_CONFIGURED)。用户之前已经询问过USBD_CONFIGURED,所以这次的问题可能是他们在开发过程中遇到了设备状态无法更新的问题。 接下来,我需要分析可能的原因。根据之前回答的USBD_CONFIGURED状态,设备在配置成功后应该更新状态,所以cur_status可能对应这个状态变量。可能的原因包括: 1. **设备未被正确配置**:主机没有发送SET_CONFIGURATION请求,或者设备没有正确处理该请求。 2. **端点初始化失败**:配置后需要初始化端点,如果失败,状态可能不会更新。 3. **描述符问题**:配置描述符或HID报告描述符有错误,导致主机无法正确识别设备。 4. **代码逻辑错误**:状态机跳转条件不正确,或者在配置完成后未更新cur_status。 5. **硬件问题**:USB接口或电源不稳定,导致枚举过程失败。 然后,我需要考虑用户可能的开发环境,比如使用STM32的HAL库或者其他嵌入式平台。用户可能在编写USB HID设备固件,使用C语言,并遇到了状态无法更新的问题。 接下来,我应该提供具体的排查步骤,帮助用户逐步解决问题。比如: - **检查描述符**:确认配置描述符和HID报告描述符是否符合规范,没有语法错误或长度错误。 - **调试主机请求**:使用工具监控USB通信,确认主机是否发送了SET_CONFIGURATION请求,设备是否正确响应。 - **验证端点初始化**:在代码中检查端点初始化的返回值,确认是否成功。 - **状态机逻辑**:审查状态机代码,确保在配置请求处理时正确更新cur_status。 - **电源和连接**:排除硬件问题,比如USB线缆接触不良或供电不足。 同时,用户可能需要具体的代码示例或工具推荐,比如使用Wireshark或USBlyzer抓包分析,或者添加调试日志输出cur_status的变化情况。 另外,需要提醒用户注意常见错误点,比如描述符中的端点地址冲突,或者未正确实现HID类请求(如Get_Report)。此外,还要考虑USB库的版本兼容性,是否存在已知的bug。 最后,总结可能的解决步骤,帮助用户系统地排查问题,从软件到硬件,从代码到工具,确保覆盖所有可能的原因。</think>当`usb_hid.cur_status`(或类似状态变量)一直为`0`时,通常表示USB HID设备未能成功进入**已配置(Configured)状态**,或状态变量未被正确更新。以下是逐步排查和解决思路: --- ### **1. 理解`cur_status`的含义** - **典型定义**:在USB HID固件代码中,`cur_status`可能表示设备当前状态(如`USBD_STATE_DEFAULT`、`USBD_STATE_CONFIGURED`等)。 - **常见值**: - `0`:未配置(Default/Address状态) - `1`:已配置(Configured状态) - 其他值可能表示错误或中间状态。 --- ### **2. 常见原因分析** #### **2.1 设备未完成枚举(Enumeration)** - **现象**:主机未正确识别设备,设备停留在`Default`或`Address`状态。 - **排查**: - 使用USB协议分析工具(如Wireshark + USBpcap)捕获枚举过程,检查是否收到`SET_CONFIGURATION`请求。 - 检查设备描述符(Device Descriptor)、配置描述符(Configuration Descriptor)、HID报告描述符(Report Descriptor)是否符合规范。 #### **2.2 描述符错误** - **关键点**: - **配置描述符长度不匹配**:`wTotalLength`字段需准确描述所有接口和端点的总长度。 - **端点地址冲突**:确保端点地址(如`0x81`、`0x01`)未被重复使用。 - **HID报告描述符语法错误**:使用工具(如[USB HID Descriptor Tool](https://www.usb.org/document-library/hid-descriptor-tool))验证描述符。 #### **2.3 端点初始化失败** - **代码示例**(以STM32 HAL为例): ```c // 初始化输入/输出端点 if (USBD_LL_OpenEP(&husb, HID_EPIN_ADDR, USBD_EP_TYPE_INTR, HID_EPIN_SIZE) != USBD_OK) { Error_Handler(); // 初始化失败时状态不更新 } ``` - **检查点**:确保端点类型(`USBD_EP_TYPE_INTR`)、地址、缓冲区大小与描述符一致。 #### **2.4 状态机未正确跳转** - **代码逻辑问题**:未在`SET_CONFIGURATION`请求处理函数中更新`cur_status`。 ```c void USBD_HID_SetConfiguration(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { if (cfgidx == 0) { // 取消配置 pdev->dev_state = USBD_STATE_ADDRESSED; } else { // 配置成功,更新状态 pdev->dev_state = USBD_STATE_CONFIGURED; // 确保此处赋值 InitEndpoints(pdev); // 初始化端点 } } ``` --- ### **3. 调试步骤** #### **3.1 验证描述符** - **最小化描述符**:使用最简单的HID设备描述符(如仅一个输入报告)。 ```c // HID报告描述符示例(鼠标) __ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[] __ALIGN_END = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Buttons) 0x19, 0x01, // Usage Minimum (1) 0x29, 0x03, // Usage Maximum (3) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x95, 0x03, // Report Count (3) 0x75, 0x01, // Report Size (1) 0x81, 0x02, // Input (Data, Var, Abs) // ... 其他描述符 }; ``` #### **3.2 添加调试输出** - **打印状态变化**: ```c printf("Current State: %d\n", usb_hid.cur_status); // 在状态变更时输出 ``` - **检查`SET_CONFIGURATION`请求处理**:在USB库的回调函数中添加断点或日志。 #### **3.3 检查硬件连接** - **电源稳定性**:USB供电不足可能导致枚举失败。 - **数据线质量**:更换USB线或尝试低速模式(Full Speed)。 --- ### **4. 常见错误案例** #### **案例1:端点未启用** - **现象**:`cur_status`始终为`0`,主机无法发送数据。 - **原因**:在配置描述符中声明了端点,但代码未调用`USBD_LL_OpenEP`。 - **解决**:在`USBD_HID_Init()`或`USBD_HID_SetConfiguration()`中初始化端点。 #### **案例2:HID类请求未处理** - **现象**:主机发送`GET_REPORT`请求后无响应。 - **代码补充**: ```c int8_t USBD_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { switch (req->bRequest) { case HID_REQ_GET_REPORT: // 发送报告数据 USBD_CtlSendData(pdev, report_data, report_size); break; default: USBD_CtlError(pdev, req); break; } } ``` --- ### **5. 总结** - **核心问题**:`usb_hid.cur_status`为`0`表明设备未进入`Configured`状态。 - **关键检查点**: 1. USB描述符的正确性。 2. 端点初始化是否成功。 3. 状态机是否在`SET_CONFIGURATION`请求后更新。 - **工具辅助**:协议分析工具 + 调试日志是快速定位问题的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值