I have multiple WinUsb_ReadIsochPipeAsap
in flight at a time and always queue the next one as soon as WinUsb_GetOverlappedResult
succeeds for the first. According to the 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.
(emphasis added)
When I measure the data rate on the host (rate of read completion), I am only seeing a polling rate of 6ms (every 6th frame), even though the descriptor bInterval
is 1
, and 6ms isn't even a possible polling interval for isochronous endpoints. The documentation makes me think I should see a transfer on every frame, because I always have multiple pending transfers.
If I look at the bus with a USB analyzer, I don't see any IN-NAK
from the device, so I suspect Windows is not even making the request.
Why don't I see the full 1000Hz transfer rate?
The arguments to 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
);
According to the documentation, the meaning of ContinueStream
is:
ContinueStream [in]
Indicates that the transfer should only be submitted if it can be scheduled in the first frame after the last pending transfer.
(emphasis added)
And this is true. For example, if your very first call to WinUsb_ReadIsochPipeAsap
passes TRUE
, you will get ERROR_INVALID_PARAMETER
because there is nothing to continue. If you allow more than 1 frame of time (1ms in full speed) and pass TRUE
, you will get ERROR_INVALID_PARAMETER
because the "first frame after the last pending transfer" is no longer available.
However, despite the very similar language about "first frame after the last pending transfer" being the default behavior, the only way Windows will actually schedule the transfers to be in sequential frames is if ContinueStream
is set to TRUE
. It doesn't matter how fast you call WinUsb_ReadIsochPipeAsap
or how many overlapped calls you make.
The solution is to always try to ContinueStream
, but fall back to not continuing in case there is a hitch (else you will fail forever after the first failed ContinueStream
):
ContinueStream = FALSE;
while (...)
{
if ( !WinUsb_ReadIsochPipeAsap( ..., ContinueStream, ... ) )
{
DWORD lastError = GetLastError();
if ( lastError == ERROR_INVALID_PARAMETER && ContinueStream)
{
ContinueStream = FALSE;
continue;
}
}
ContinueStream = TRUE;
...
}
Alternatively, you could rewrite your loop to use WinUsb_ReadIsochPipe
(the non ASAP version), but that just requires you to manage the sequential frame numbers yourself.