为什么我不能从 WinUsb_ReadIsochPipeAsap 获得全部带宽?

Why can't I get full bandwidth from WinUsb_ReadIsochPipeAsap?

我一次有多个 WinUsb_ReadIsochPipeAsap 在飞行,并且总是在第一个 WinUsb_GetOverlappedResult 成功后立即排队下一个。根据 documentation:

WinUsb_ReadIsochPipeAsap allows the USB driver stack to choose the starting frame number for the transfer. If one or more transfers are already pending on the endpoint, the transfer will be scheduled for the frame number immediately following the last frame number of the last currently pending transfer.

(强调)

当我测量主机上的数据速率(读取完成速率)时,我只看到 6 毫秒的轮询速率(每 6 帧),即使描述符 bInterval1,而 6ms 甚至不是等时端点的可能轮询间隔。文档让我觉得我应该在每一帧上看到一个传输,因为我总是有多个待处理的传输。

如果我用 USB 分析仪查看总线,我没有从设备中看到任何 IN-NAK,所以我怀疑 Windows 甚至没有发出请求。

为什么我看不到完整的 1000Hz 传输速率?

WinUsb_ReadIsochPipeAsap的参数:

BOOL __stdcall WinUsb_ReadIsochPipeAsap(
  _In_     PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle,
  _In_     ULONG                       Offset,
  _In_     ULONG                       Length,
  _In_     BOOL                        ContinueStream,
  _In_     PULONG                      NumberOfPackets,
           PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors,
  _In_opt_ LPOVERLAPPED                Overlapped
);

根据documentationContinueStream的含义是:

ContinueStream [in]

Indicates that the transfer should only be submitted if it can be scheduled in the first frame after the last pending transfer.

(强调)

这是真的。例如,如果您对 WinUsb_ReadIsochPipeAsap 的第一次调用通过了 TRUE,您将得到 ERROR_INVALID_PARAMETER,因为没有任何内容可以继续。如果允许超过 1 帧的时间(全速 1 毫秒)并通过 TRUE,您将得到 ERROR_INVALID_PARAMETER,因为 "first frame after the last pending transfer" 不再可用。

然而,尽管关于 "first frame after the last pending transfer" 的语言非常相似是默认行为,唯一的方法 Windows 将实际安排如果 ContinueStream 设置为 TRUE,则在连续帧中进行传输。无论您拨打多快 WinUsb_ReadIsochPipeAsap 或您拨打多少次重叠电话都无关紧要。

解决方案是始终尝试 ContinueStream,但在出现故障时回退到不继续(否则您将在第一次失败后永远失败 ContinueStream):

ContinueStream = FALSE;

while (...)
{
    if ( !WinUsb_ReadIsochPipeAsap( ..., ContinueStream, ... ) )
    {
        DWORD lastError = GetLastError();

        if ( lastError == ERROR_INVALID_PARAMETER && ContinueStream)
        {
            ContinueStream = FALSE;
            continue;
        }
    }
    ContinueStream = TRUE;
    ...
}

或者,您可以重写循环以使用 WinUsb_ReadIsochPipe(非 ASAP 版本),但这只需要您自己管理顺序帧编号。