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 值如何影响事物的明确解释。
显然,这是驱动程序强制执行的。
所以是:
驱动程序禁止触发请求,直到 8 毫秒过去。对我来说这听起来是最合理的选择,但从我的 URB Trace 来看,Dispatch message
事件在请求返回前几毫秒引发。这意味着离开主机的实时数据对 me/the 消息分析器是隐藏的。
驱动程序对我和分析器隐藏响应,直到自上次响应后 8 毫秒过去。
如果确实由驱动程序处理,则交换消息日志中向我显示的某处存在谎言。响应应该在请求之后立即出现,但事实并非如此。因此,请求要么在显示时间之后发送,要么响应比显示时间早。
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。
我有一个发送报告描述符的全速 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 值如何影响事物的明确解释。
显然,这是驱动程序强制执行的。
所以是:
驱动程序禁止触发请求,直到 8 毫秒过去。对我来说这听起来是最合理的选择,但从我的 URB Trace 来看,
Dispatch message
事件在请求返回前几毫秒引发。这意味着离开主机的实时数据对 me/the 消息分析器是隐藏的。驱动程序对我和分析器隐藏响应,直到自上次响应后 8 毫秒过去。
如果确实由驱动程序处理,则交换消息日志中向我显示的某处存在谎言。响应应该在请求之后立即出现,但事实并非如此。因此,请求要么在显示时间之后发送,要么响应比显示时间早。
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。