USB 外围设备的 bIntervals 是如何执行的?

How are USB peripherals' bIntervals enforced?

我有一个发送报告描述符的全速 USB 设备,其相关端点描述符声明 bInterval 为 8,即 8 毫秒。

以下报告摘录是在设备驱动程序为 HidUsb 时从 USB 描述符转储程序中获取的:

Interface Descriptor: // +several attributes
------------------------------
0x04    bDescriptorType
0x03    bInterfaceClass      (Human Interface Device Class)
0x00    bInterfaceSubClass   
0x00    bInterfaceProtocol   
0x00    iInterface

HID Descriptor: // +bLength, bCountryCode
------------------------------
0x21    bDescriptorType
0x0110  bcdHID
0x01    bNumDescriptors
0x22    bDescriptorType   (Report descriptor)
0x00D6  bDescriptorLength

Endpoint Descriptor: // + bLength, bEndpointAddress, wMaxPacketSize
------------------------------
0x05    bDescriptorType
0x03    bmAttributes      (Transfer: Interrupt / Synch: None / Usage: Data)
0x08    bInterval         (8 frames)

在将驱动程序切换到 WinUSB 以便能够使用它之后,如果我使用 libusb 重复查询 IN 中断传输,并使用此脚本计算 2 次 libusb 调用之间和 libusb 调用期间花费的实际时间:

for (int i = 0; i < n; i++) {
    start = std::chrono::high_resolution_clock::now();
    forTime = (double)((start - end).count()) / 1000000;

    <libusb_interrupt_transfer on IN interrupt endpoint>

    end = std::chrono::high_resolution_clock::now();
    std::cout << "for " << forTime << std::endl;

    transferTime = (double)((end - start).count()) / 1000000;
    std::cout << "transfer " << transferTime << std::endl;

    std::cout << "sum " << transferTime + forTime << std::endl << std::endl;
}

这是获得值的示例:

for 2.60266
transfer 5.41087
sum 8.04307     //~8

for 3.04287
transfer 5.41087
sum 8.01353     //~8

for 6.42174
transfer 9.65907
sum 16.0808     //~16

for 2.27422
transfer 5.13271
sum 7.87691     //~8

for 3.29928
transfer 4.68676
sum 7.98604     //~8

总和值始终非常接近 8 毫秒,除非在启动新的中断传输调用之前经过的时间太长(对于我的特定情况,阈值似乎在 6 到 6.5 之间),在这种情况下它是相等的到 16。我曾经看到 "for" 测量等于 18 毫秒,总和恰好等于 24 毫秒。使用 URB 跟踪器(在我的例子中是 Microsoft Message Analyzer),Complete URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 消息之间的时间差也是 8 毫秒的倍数——通常是 8 毫秒。简而言之,它们符合 "sum" 措施。

所以,很明显 "libusb interrupt transfer calls" 的两个 returns 之间经过的时间是 8ms 的倍数,我假设这与 bInterval 值 8 有关(FullSpeed -> *1ms -> 8 毫秒)。

但我希望,现在我已经清楚地说明了我在说什么 - 这是在哪里强制执行的?尽管进行了研究,但我找不到 bInterval 值如何影响事物的明确解释。

显然,这是驱动程序强制执行的。

所以是:

如果确实由驱动程序处理,则交换消息日志中向我显示的某处存在谎言。响应应该在请求之后立即出现,但事实并非如此。因此,请求要么在显示时间之后发送,要么响应比显示时间早。

bInterval 的执行情况如何?

我的最终目标是忽略 bInterval 值并比 8ms 更频繁地轮询设备(我有充分的理由相信它可以每 2ms 轮询一次,并且 8ms 的周期对于它的使用是不可接受的),但首先我想知道它的当前限制是如何工作的,如果我正在寻找的东西是可能的,那么我可以理解接下来要学习的内容(例如编写自定义 WinUSB 驱动程序)

I have a FullSpeed USB Device

注意:你验证了吗? 8 毫秒是 速度 USB 设备的限制 - 许多常见的鼠标或键盘可能仍在使用。

8 毫秒调度是在 USB 主机驱动程序 (ehci/xhci) AFAIK 内部完成的。您可以尝试通过释放和回收接口来进行赌博——尽管未经过测试。 (编辑:不起作用,请参阅评论)。

USB 设备无法自行通信,因此必须是延迟的请求。请注意,当没有新数据可用时,设备也可以 NAK 任何中断 IN 请求。这只是在时间上增加了另一个 bInterval 毫秒。

writing a custom WinUSB driver

不推荐 - 替换 windows 提供的驱动程序非常麻烦。我们用于 USB CDC 设备的 libusb-win32 替换器在所有大型 windows 10 次升级中均中断 - 升级完成后,该设备使用 COM 端口而不是 libusb。