我正在尝试为 USB (1.1) HID 设备 using an Arudino board and a CH376 module 创建原型。我设法编写了将 CH376 设置为设备模式并处理所有 USB 事务的 Arduino 代码。
只要我声明该设备属于“供应商特定”类,Windows 10 就会将该设备识别为“未知设备”,并在我在控制面板(设备属性 - 详细信息 - 硬件 ID)。到现在为止还挺好。然后我可以使用 Zadig 为设备分配一个 libusb 驱动程序,然后它会显示在控制面板的“libusb 设备”类别下;一切都还好,我可以使用 libusb 函数与设备通信。
现在问题来了。如果我尝试使用 WinUSB 而不是 libusb 来管理设备,当我插入或重置设备时会发生以下情况:
Windows 做了 the usual device initialization sequence:它请求设备描述符(64 个字节),然后给设备分配一个地址,然后再次请求设备描述符(这次只有 18 个字节),然后请求配置描述符。
五秒后,Windows 再次请求设备描述符。这就是设备出现在设备管理器中的位置,状态为“此设备工作正常”。
再过 5 秒后,Windows 再次请求配置描述符,但在仅检索前 8 个字节(即控制端点的大小)后,它断开设备连接并在设备管理器中用感叹号显示它。
如果我转到设备属性,“常规”选项卡,它会说:
This device cannot start. (Code 10)
{Device Timeout}
The specified I/O operation on %hs was not completed before the time-out period expired.
如果我转到“事件”选项卡,最后一个是“设备未启动 (WinUSB)”,其中包含以下详细信息:
Device USB\VID_1209&PID_0002\8&339ad878&0&2 had a problem starting.
Driver Name: oem168.inf
Class Guid: {88bae032-5a81-49f0-bc3d-a4ff138216d6}
Service: WinUSB
Lower Filters:
Upper Filters:
Problem: 0xA
Problem Status: 0xC00000B5
现在,如果不是将设备声明为属于“供应商特定”类,而是将其声明为 HID 设备并让 Windows 选择正确的驱动程序,则行为是相同的,但“常规”中的错误消息标签是:
This device cannot start. (Code 10)
The I/O request was canceled.
...以及最后一个事件:
Device USB\VID_1209&PID_0002\8&339ad878&0&2 had a problem starting.
Driver Name: input.inf
Class Guid: {745a17a0-74d3-11d0-b6fe-00a0c90f57da}
Service: HidUsb
Lower Filters:
Upper Filters:
Problem: 0xA
Problem Status: 0xC0000120
我怀疑原型对于 Windows 内置的 USB 设备管理库来说可能太慢了,而 libusb 更宽松,但我觉得这很奇怪(Arduino 板上的代码只是检查来自 CH376 的事件并处理它们立即循环),我想知道我是否做错了什么。
这是 Arduino 代码生成的跟踪的转储。 SETUP/IN/OUT 事件表示令牌处理已经完成;对于 IN 令牌,我之前写入的任何数据(“写入 X 字节”日志)都已发送。通过使用 libusb,我可以确认记录的数据确实是我发送的数据。
Int: USB_SUSPEND
Int: WAKE_UP
Int: WAKE_UP
Int: EP0_SETUP
0x80 0x06 0x00 0x01 0x00 0x00 0x40 0x00
GET_DESCRIPTOR: DEVICE
Writing 8 bytes: 0x12 0x01 0x10 0x01 0x00 0x00 0x00 0x08
Int: EP0_IN
Writing 8 bytes: 0x09 0x12 0x02 0x00 0x00 0x01 0x01 0x02
Int: EP0_OUT
Int: EP0_OUT
Int: EP0_OUT
Int: EP0_OUT
Int: EP0_OUT
Int: EP0_SETUP
0x00 0x05 0x18 0x00 0x00 0x00 0x00 0x00
SET_ADDRESS: 24
Int: EP0_IN
Setting address: 24
Int: EP0_SETUP
0x80 0x06 0x00 0x01 0x00 0x00 0x12 0x00
GET_DESCRIPTOR: DEVICE
Writing 8 bytes: 0x12 0x01 0x10 0x01 0x00 0x00 0x00 0x08
Int: EP0_IN
Writing 8 bytes: 0x09 0x12 0x02 0x00 0x00 0x01 0x01 0x02
Int: EP0_IN
Writing 2 bytes: 0x00 0x01
Int: EP0_IN
Int: EP0_OUT
Int: EP0_SETUP
0x80 0x06 0x00 0x02 0x00 0x00 0xFF 0x00
GET_DESCRIPTOR: CONFIGURATION
Writing 8 bytes: 0x09 0x02 0x22 0x00 0x01 0x01 0x00 0x80
Int: EP0_IN
Writing 8 bytes: 0x32 0x09 0x04 0x00 0x00 0x01 0x03 0x00
Int: EP0_IN
Writing 8 bytes: 0x00 0x00 0x09 0x21 0x10 0x01 0x00 0x01
Int: EP0_IN
Writing 8 bytes: 0x22 0x29 0x00 0x07 0x05 0x81 0x03 0x03
Int: EP0_IN
Writing 2 bytes: 0x00 0x0A
Int: EP0_IN
Int: EP0_OUT
(暂停 5 秒,设备管理器中还没有条目)
Int: EP0_SETUP
0x80 0x06 0x00 0x01 0x00 0x00 0x12 0x00
GET_DESCRIPTOR: DEVICE
Writing 8 bytes: 0x12 0x01 0x10 0x01 0x00 0x00 0x00 0x08
Int: EP0_IN
Writing 8 bytes: 0x09 0x12 0x02 0x00 0x00 0x01 0x01 0x02
Int: EP0_IN
Writing 2 bytes: 0x00 0x01
Int: EP0_IN
Int: EP0_OUT
(再次暂停 5 秒,设备在设备管理器中显示工作正常)
Int: EP0_SETUP
0x80 0x06 0x00 0x02 0x00 0x00 0x09 0x00
GET_DESCRIPTOR: CONFIGURATION
Writing 8 bytes: 0x09 0x02 0x22 0x00 0x01 0x01 0x00 0x80
Int: USB_SUSPEND
(设备现在显示为感叹号)
顺便说一下,我通过写入串行端口来获取这些痕迹,然后由计算机读取。考虑到这可能会减慢整个处理速度,我尝试禁用任何串行端口通信,结果是一样的。
所以我的问题是:
编辑
如果我使用 Microsoft's USB hardware verifier,我会得到以下输出:
Event Message: USBXHCI Device Update
VendorID/ProductID: 0x1209/0x2
PortPath: 0x4, 0x4, 0x4, 0x1, 0x0, 0x0
HWVerify Errors Encountered so far:
#1: (UsbHub3/171): Request for Language ID String Descriptor Failed
#2: (UsbHub3/132): Device Control Transfer Error
...这很奇怪,因为设备似乎根本没有收到任何对字符串描述符的请求!
编辑 2
根据评论中的建议,我将 Wireshark 与 USBPcap 一起使用,我看到的是:
这与我在自己的跟踪中看到的最后两个请求一致。
编辑 3
为了不让问题文本增长太多I have created a gist包含:
这是我正在使用的 CH376 模块的原理图(这是我找到的有关该模块的所有技术信息):
编辑 4
正如评论中所建议的,我修改了我的代码,以便设备现在将自己报告为 USB 2.0 设备并提供序列号字符串。现在从设备的角度来看事件的顺序是:
有趣的是,Wireshark 跟踪完全相同:语言请求和 Microsoft 特定的描述符请求没有显示!
这种模式似乎是 Wireshark 看不到 5 秒暂停后的请求,所以也许它们没有被我的代码正确处理,也没有被主机认为是完成的,但我不明白为什么(因为我正在执行与第二个获取设备描述符请求相同的处理,该请求成功)。
另外值得注意的是,当第一个设备描述符请求完成时,现在我得到的不是一个而是六个 OUT 令牌中断。这可能是时间问题的征兆吗?
编辑 5
显然是时间问题。
我使用 Zadig 将设备的驱动程序设置为 libusb,然后我尝试在循环中发送一些描述符请求;结果是,有二分之一的请求始终失败。但是,如果我在请求之间放置 1 毫秒的延迟,那么它们都可以正常工作!就好像芯片在成功请求后需要一个“冷却期”。失败的请求不会出现在 Wireshark 或我自己的日志中,因此似乎 SETUP 令牌本身丢失了。
有什么方法可以指示 Windows 在对给定设备的请求之间稍等片刻?
答案 0 :(得分:3)
我检查了您的代码并发现:
#define USB_REQ_SET_CONFIGURATION 是 9 而不是 8
在通过 SPI 驱动 CH376s 的 Teensy2 上,它在我的 Mac 上正确显示如下:
NestorDevice:
Product ID: 0x0002
Vendor ID: 0x1209
Version: 1.00
Speed: Up to 12 Mb/s
Location ID: 0x14223000 / 48
Current Available (mA): 500
Current Required (mA): 100
Extra Operating Current (mA): 0
我还没有尝试与它沟通。
还要知道时机就是一切。当我在 Mac 上运行您的代码时,通过串行将所有命令中继到 Teensy,我错过了某些事件。 Mac 发出所有命令的速度非常快,串行延迟太多。
编辑
有两种类型的读取:
CMD01_RD_USB_DATA0 EQU 027H ; / * Read the data block from the current USB interrupt endpoint buffer or the host endpoint receive buffer * /
; / * Output: length, data stream * /
CMD01_RD_USB_DATA EQU 028H ; / * Device mode: Read the data block from the current USB interrupt endpoint buffer and release the buffer, equivalent to CMD01_RD_USB_DATA0 + CMD00_UNLOCK_USB * /
; / * Output: length, data stream * /
如果我们使用后一个函数呢?它会更早地释放缓冲区并为下一个事件做好准备吗?我会在这里尝试一下,看看它是否有意义。
答案 1 :(得分:1)
我一直在对此进行进一步调查,并在 Github 上找到了更多中文示例。令我震惊的是,他们在 EP0_IN 处添加了数量不等的 UNLOCK_USB 命令。
一旦我这样做了,我就更幸运地注册了串行设备。否则它会处于循环中,每 5 秒重试一次。
所以在 USB_INT_EP0_IN 处添加一个额外的 CMD_UNLOCK_USB 在处理任何需要的处理之后。查看更多here。