winusb driver - isochronous stream transmission get USBD_STATUS_ISO_NOT_ACCESSED_BY_HW from WinUsb_G

Hi,

We are using our own usb driver for our device transmitting analog data 2bytes per 1ms via iso pipe.
Driver written 15 years ago and in some PC’s do not work properly.

I wanted to check possibility to work with winusb.sys driver.

I installed it OK, Can send commands via control pipes but when i start isochronous pipe stream i get USBD_STATUS_ISO_NOT_ACCESSED_BY_HW
error in “isochPacketDescriptors[j].Status” for all transfers exept the first one…

Here is a code

define ISOCH_DATA_SIZE_MS 10
define ISOCH_TRANSFER_COUNT 2 //will need to be at least 2

/
My device sends one sample of 2 bytes every 1ms … => data sample rate = 1000s/s

Problem !!

Only Transfer 1 has the data all next (ISOCH_TRANSFER_COUNT) are empty

As a result i see data arriving at sample rate of 1000 / ISOCH_TRANSFER_COUNT in Read application

Why ???

nvUSB.lib: Current Frame number=261744397
nvUSB.lib: bRead() Requested 1200 bytes in 50 packets per transfer.
nvUSB.lib: bRead() Transfer 1 completed. Read 100 bytes.
nvUSB.lib: Current Frame number=261744447
nvUSB.lib: bRead() Requested 1200 bytes in 50 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes. ← return with error USBD_STATUS_ISO_NOT_ACCESSED_BY_HW , why??
nvUSB.lib: Current Frame number=261744497
nvUSB.lib: bRead() Requested 1200 bytes in 50 packets per transfer.
nvUSB.lib: bRead() Transfer 1 completed. Read 100 bytes.
nvUSB.lib: Current Frame number=261744547
nvUSB.lib: bRead() Requested 1200 bytes in 50 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes. ← return with error USBD_STATUS_ISO_NOT_ACCESSED_BY_HW , why??
nvUSB.lib: Current Frame number=261744597
nvUSB.lib: bRead() Requested 1200 bytes in 50 packets per transfer.
nvUSB.lib: bRead() Transfer 1 completed. Read 100 bytes.
nvUSB.lib: Current Frame number=261744647
nvUSB.lib: bRead() Requested 1200 bytes in 50 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=261744697

*/

Start this thread when user wants start the data stream…

DWORD WINAPI CUsbPCECGHelper::AsyncReadThreadProc(__in LPVOID pvData)
{

if(1)

//--------------
HANDLE hEvents[ISOCH_TRANSFER_COUNT+1];
DWORD dwWait = 0;
DWORD dwNum = 0;
USHORT headerSize = 3;
ULONG bytesRead = 0;
BOOL b = FALSE;
ULONG i;
BOOL result = FALSE;
// Cast the argument to the correct type
CUsbPCECGHelper* pThis = static_cast<CUsbPCECGHelper*>(pvData);
// New threads must always CoInitialize
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
ULONG totalTransferSize = pThis->IsochInTransferSize * ISOCH_TRANSFER_COUNT;
pThis->readBuffer = new UCHAR[totalTransferSize];
if (pThis->readBuffer == NULL)
{
//sprintf_s(s, sizeof(s), “nvUSB.lib: bStart() Unable to allocate memory\n”);
//OutputDebugString((LPCSTR)s);
hr = E_UNEXPECTED;
}
else
{
ZeroMemory(pThis->readBuffer, totalTransferSize);
}
hEvents[0] = pThis->m_hCloseThread;
for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
{
pThis->m_hReadAsync[i] = CreateEvent(
NULL, // default security attribute
TRUE, // manual-reset event
FALSE, // initial state = not signaled
NULL); // unnamed event object
}
for (i = 1; i < (ISOCH_TRANSFER_COUNT+1); i++)
hEvents[i] = pThis->m_hReadAsync[i-1];
if (SUCCEEDED(hr))
{
pThis->overlapped = new OVERLAPPED[ISOCH_TRANSFER_COUNT];
ZeroMemory(pThis->overlapped, (sizeof(OVERLAPPED) * ISOCH_TRANSFER_COUNT));
for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
{
// pThis->overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

     pThis->overlapped[i].hEvent = pThis->m_hReadAsync[i];
     // Initialize the rest of the OVERLAPPED structure to zero.
     pThis->overlapped[i].Internal = 0;
     pThis->overlapped[i].InternalHigh = 0;
     pThis->overlapped[i].Offset = 0;
     pThis->overlapped[i].OffsetHigh = 0;
     if (pThis->overlapped[i].hEvent == NULL)
     {
         //sprintf_s(s, sizeof(s), "nvUSB.lib: bStart() Unable to set event for overlapped operation\n");
         //OutputDebugString((LPCSTR)s);
         hr = E_UNEXPECTED;
     }
 }

}
if (SUCCEEDED(hr))
{
result = WinUsb_RegisterIsochBuffer(
pThis->m_hWinUSBHandle[0], // An opaque handle to an interface in the selected configuration. That handle must be created by a previous call to WinUsb_Initialize or WinUsb_GetAssociatedInterface.
pThis->PipeID.Iso_Pipe[0].PipeId, // Derived from Bit 3…0 of the bEndpointAddress field in the endpoint descriptor.
pThis->readBuffer, // Pointer to the transfer buffer to be registered.
totalTransferSize, // Length, in bytes, of the transfer buffer pointed to by Buffer.
&pThis->isochReadBufferHandle); // Receives an opaque handle to the registered buffer. This handle is required by other WinUSB functions that perform isochronous transfers. To release the handle, call the WinUsb_UnregisterIsochBuffer function.
if (!result)
{
//sprintf_s(s, sizeof(s), “nvUSB.lib: bStart() Isoch buffer registration failed.\n”);
//OutputDebugString((LPCSTR)s);
hr = E_UNEXPECTED;
}
}
if (SUCCEEDED(hr))
{
pThis->isochPacketDescriptors = new USBD_ISO_PACKET_DESCRIPTOR[pThis->IsochInPacketCount * ISOCH_TRANSFER_COUNT];
ZeroMemory(pThis->isochPacketDescriptors, pThis->IsochInPacketCount * ISOCH_TRANSFER_COUNT);
//for iso endpoints read this
//https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/getting-set-up-to-use-windows-devices-usb
//
for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
{
result = WinUsb_ReadIsochPipeAsap(
pThis->isochReadBufferHandle, // An opaque handle to the transfer buffer that was registered by a previous call to WinUsb_RegisterIsochBuffer.
pThis->IsochInTransferSize * i, // Offset into the buffer relative to the start the transfer.
pThis->IsochInTransferSize, // Length in bytes of the transfer buffer.
(i == 0) ? FALSE : TRUE, // Indicates that the transfer should only be submitted if it can be scheduled in the first frame after the last pending transfer.
pThis->IsochInPacketCount, // Total number of isochronous packets required to hold the transfer buffer.Also indicates the number of elements in the array pointed to by IsoPacketDescriptors.
&pThis->isochPacketDescriptors[i * pThis->IsochInPacketCount], //An array of USBD_ISO_PACKET_DESCRIPTOR that receives the details of each isochronous packet in the transfer.
&pThis->overlapped[i]); // Pointer to an OVERLAPPED structure used for asynchronous operations.
if (!result)
{
DWORD lastError = GetLastError();
if (lastError != ERROR_IO_PENDING)
{
//sprintf_s(s, sizeof(s), “nvUSB.lib: bStart() Failed to start a read operation with error %x\n”, lastError);
//OutputDebugString((LPCSTR)s);
hr = E_UNEXPECTED;
}
}
}
}
//--------------
if (SUCCEEDED(hr))
{
while (true)
{
for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
pThis->m_bResult = ResetEvent(pThis->m_hReadAsync[i]);
dwWait = WaitForMultipleObjects(ISOCH_TRANSFER_COUNT+1, // number of event objects
hEvents, // array of event objects
FALSE, // does not wait for all
INFINITE // waits indefinitely
);

     if (dwWait == WAIT_OBJECT_0) //STOP_THREAD
     {
         CoUninitialize();
         for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
             CloseHandle(pThis->m_hReadAsync[i]);
         return 0;
     }
     else
     {
         for (i = 1; i < (ISOCH_TRANSFER_COUNT + 1); i++)
         {
             if (dwWait == (WAIT_OBJECT_0 + i))
             {
                 pThis->ReadIsoBuffer((i-1));
             }
         }
     }
 }

}

endif

continue …

ReadIsoBuffer code

DWORD CUsbPCECGHelper::ReadIsoBuffer(DWORD eventIndex)
{

if(1)

/*
//for iso endpoints read this
//https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/getting-set-up-to-use-windows-devices-usb
//https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/transfer-data-to-isochronous-endpoints
//
*/
char s[256];
DWORD Status = UsbPCECG_ERR_SUCCESS;
ULONG i, j;
BOOL result;
DWORD lastError;
ULONG numBytes = 0;
BOOL ContinueStream = TRUE;
ULONG frameNumber;
ULONG startFrame;
LARGE_INTEGER timeStamp;
if (helperfunctionsc_class.m_hWinUSBHandle[0] == INVALID_HANDLE_VALUE)
{
return UsbPCECG_ERR_READISOBUFF;
}
result = WinUsb_GetCurrentFrameNumber(
helperfunctionsc_class.m_hWinUSBHandle[0],
&frameNumber,
&timeStamp);
if (!result)
{
sprintf_s(s, sizeof(s), “nvUSB.lib: bRead() WinUsb_GetCurrentFrameNumber failed. \n”);
OutputDebugString((LPCSTR)s);
return UsbPCECG_ERR_READISOBUFF;
}
sprintf_s(s, sizeof(s), “nvUSB.lib: Current Frame number=%d\n”, frameNumber);
OutputDebugString((LPCSTR)s);
startFrame = frameNumber + 10;
i = eventIndex;
// for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
{
if (helperfunctionsc_class.RealTimeInProgress == FALSE)
{
return UsbPCECG_ERR_READISOBUFF;
}

 result = WinUsb_GetOverlappedResult(
     helperfunctionsc_class.m_hWinUSBHandle[0],
     &helperfunctionsc_class.overlapped[i],
     &numBytes,
     TRUE);
 if (!result || numBytes == 0)
 {
     //0x1F  = ERROR_GEN_FAILURE                A device attached to the system is not functioning.
     //0x7A  = ERROR_INSUFFICIENT_BUFFER     The data area passed to a system call is too small.
     //0x57  = ERROR_INVALID_PARAMETER       The parameter is incorrect.
     //0x3E5 = ERROR_IO_PENDING              Overlapped I/O operation is in progress.
     lastError = GetLastError();
     if (lastError == ERROR_IO_PENDING) //The transfer is pending..
     {
         // try again..
         if (i > 0) i--;

// continue;
}

     sprintf_s(s, sizeof(s), "nvUSB.lib: bRead() Failed to read with error %x \n", lastError);
     OutputDebugString((LPCSTR)s);
     return UsbPCECG_ERR_READISOBUFF;
 }
 else
 {
     numBytes = 0;
     for (j = 0; j < helperfunctionsc_class.IsochInPacketCount; j++)
     {
         numBytes += helperfunctionsc_class.isochPacketDescriptors[j].Length;
         //sprintf_s(s, sizeof(s), "%d-%d-%d\n", helperfunctionsc_class.isochPacketDescriptors[j].Length, helperfunctionsc_class.isochPackets[j].Offset, helperfunctionsc_class.isochPackets[j].Status);
         //OutputDebugString((LPCSTR)s);
         /*
         if (helperfunctionsc_class.isochPacketDescriptors[j].Length != 0 && helperfunctionsc_class.isochPackets[j].Status == 0)
         {
             memcpy(inBuffer, helperfunctionsc_class.readBuffer + helperfunctionsc_class.isochPacketDescriptors[j].Offset, helperfunctionsc_class.isochPacketDescriptors[j].Length);
             *nWriten += helperfunctionsc_class.isochPacketDescriptors[j].Length;
             inBuffer += helperfunctionsc_class.isochPacketDescriptors[j].Length;
         }
         */
         /*
         !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
         sprintf_s(s, sizeof(s), "nvUSB.lib: desc.lenght=%d bytes descr.status=%x\n", helperfunctionsc_class.isochPacketDescriptors[j].Length, helperfunctionsc_class.isochPacketDescriptors[j].Status);
         OutputDebugString((LPCSTR)s);
         for ECGUSB1D-EX get
         nvUSB.lib: desc.lenght=2 bytes descr.status=0           if OK
         nvUSB.lib: desc.lenght=0 bytes descr.status=c0020000    if empty packet
         WD_USBD_STATUS_ISO_NOT_ACCESSED_BY_HW
         Extended isochronous error codes returned by USBD.
         These errors appear in the packet status field of an isochronous transfer. [0xC0020000]
         For some reason the controller did not access the TD associated with this packet:
         !
         When the USB driver stack processes the URB, the driver discards all isochronous packets in the URB whose frame numbers are lower than the current frame number.
         The driver stack sets the Status member of the packet descriptor for each discarded packet to USBD_STATUS_ISO_NA_LATE_USBPORT, USBD_STATUS_ISO_NOT_ACCESSED_BY_HW,
         or USBD_STATUS_ISO_NOT_ACCESSED_LATE. Even though some packets in the URB are discarded, the driver stack attempts to transmit only those packets whose frame numbers
         are higher than the current frame number.
         !!
         */
         /*
         if (helperfunctionsc_class.isochPacketDescriptors[j].Length == 0 && helperfunctionsc_class.isochPacketDescriptors[j].Status == USBD_STATUS_ISO_NOT_ACCESSED_BY_HW)
         {
             if (!(WinUsb_FlushPipe(helperfunctionsc_class.m_hWinUSBHandle[0], helperfunctionsc_class.PipeID.Iso_Pipe[0].PipeId)))
             {
                 sprintf_s(s, sizeof(s), "nvUSB.lib: bRead() error flush pipe %d.\n", GetLastError());
                 OutputDebugString((LPCSTR)s);
                 return    UsbPCECG_ERR_RESETPIPE;
             }
         }
         */
         if (helperfunctionsc_class.isochPacketDescriptors[j].Length != 0 && helperfunctionsc_class.isochPacketDescriptors[j].Status == 0)
         {
             WriteRingBuffer(
                 helperfunctionsc_class.m_DataRingBuffer,
                 (helperfunctionsc_class.readBuffer + helperfunctionsc_class.isochPacketDescriptors[j].Offset),
                 helperfunctionsc_class.isochPacketDescriptors[j].Length
             );
         }
     }
     sprintf_s(s, sizeof(s), "nvUSB.lib: bRead() Requested %d bytes in %d packets per transfer.\n", helperfunctionsc_class.IsochInTransferSize, helperfunctionsc_class.IsochInPacketCount);
     OutputDebugString((LPCSTR)s);
 }
 sprintf_s(s, sizeof(s), "nvUSB.lib: bRead() Transfer %d completed. Read %d bytes. \n", i + 1, numBytes);
 OutputDebugString((LPCSTR)s);
 result = WinUsb_ReadIsochPipeAsap(
     helperfunctionsc_class.isochReadBufferHandle,
     helperfunctionsc_class.IsochInTransferSize * i,
     helperfunctionsc_class.IsochInTransferSize,
     ContinueStream,
     helperfunctionsc_class.IsochInPacketCount,
     &helperfunctionsc_class.isochPacketDescriptors[i * helperfunctionsc_class.IsochInPacketCount],
     &helperfunctionsc_class.overlapped[i]);
 /*
 // not working
 // return with nvUSB.lib: bRead() Failed to read with error 1f  after first time..
 result = WinUsb_ReadIsochPipe(
     helperfunctionsc_class.isochReadBufferHandle,
     helperfunctionsc_class.IsochInTransferSize * i,
     helperfunctionsc_class.IsochInTransferSize,
     &startFrame,
     helperfunctionsc_class.IsochInPacketCount,
     &helperfunctionsc_class.isochPacketDescriptors[i * helperfunctionsc_class.IsochInPacketCount],
     &helperfunctionsc_class.overlapped[i]);
 */
 if (!result)
 {
     lastError = GetLastError();
     if (lastError == ERROR_INVALID_PARAMETER && ContinueStream)
     {
         ContinueStream = FALSE;

// continue;
}

     if (lastError != ERROR_IO_PENDING)
     {
         sprintf_s(s, sizeof(s), "nvUSB.lib: bRead() Failed to start a read operation with error %x\n", lastError);
         OutputDebugString((LPCSTR)s);
         return UsbPCECG_ERR_READISOBUFF;
     }
     ContinueStream = TRUE;
 }

}
return(Status);

endif

Why on god’s green earth would you use an isochronous pipe for this? Why would you not just use the much simpler interrupt pipe that has guaranteed delivery?

What is the packet size and interval in your endpoint descriptor?

You should not be passing TRUE to ContinueStream for the second packet in your set. That flag is virtually never useful, and especially not when a device has such an ultra-low bandwidth, like yours. Just specify FALSE always. It will be scheduled as soon as possible.

Thanks Tim

Already done with “ContinueStream” parameter , first TRUE then always FALSE . Get same results
We use iso transfer , that is historically issue . We do not want change firmware of the device.
And it works fine with our current WDM driver.
I can not figure out , why it do not work with winusb driver …

Descriptor reports: 24byte maximum , 1ms interval
Firmware sends 2bytes every 1ms on SOF interrupt.

Endpoint Descriptor 81 1 In, Isochronous, 1 ms

Offset Field Size Value Description
0 bLength 1 07h
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 81h 1 In
3 bmAttributes 1 01h Isochronous, No Sync, Data
1…0: Transfer Type …01 Isochronous
7…2: Reserved 000000…
4 wMaxPacketSize 2 0018h 24 bytes
6 bInterval 1 01h 1 ms

with ISOCH_TRANSFER_COUNT=4


nvUSB.lib: bRead() Transfer 1 completed. Read 160 bytes.
nvUSB.lib: Current Frame number=864923220
nvUSB.lib: bRead() Requested 1920 bytes in 80 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=864923305
nvUSB.lib: bRead() Requested 1920 bytes in 80 packets per transfer.
nvUSB.lib: bRead() Transfer 3 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=864923390
nvUSB.lib: bRead() Requested 1920 bytes in 80 packets per transfer.
nvUSB.lib: bRead() Transfer 4 completed. Read 0 bytes.

Firmware sends 2bytes every 1ms on SOF interrupt.

That surely can’t be literally what you do. SOF is not the signal to send. You send when you get an IN token, during your timeslot. Are you just saying you shove 2 bytes into your FIFO every millisecond?

There’s a limit to the number of frames you can queue ahead. Does it work if you put, say, 16 packets per transfer?

Yep

SOF interrupt is every 1ms , send 2bytes if “rbInINPRDY” bit is fired up…

not get you about 16 packets…

//SOF interrupt
void SOFHandler(void)
{
UWRITE_BYTE(INDEX, EP1_IN); // Index to current endpoint
UREAD_BYTE(EINCSRL, bCsrL);

// If a FIFO slot is open, write a new data to the IN FIFO
if (!(bCsrL & rbInINPRDY))
{
	while(USB0ADR & 0x80);// Wait for BUSY->'0'

	// (register available)
	USB0ADR = FIFO_EP1;		// Set address
	USB0DAT = A2D_LSB;// write low byte first
	while(USB0ADR & 0x80);	// Wait for BUSY->'0' (data ready)
	USB0DAT =A2D_MSB;// write high byte
	while(USB0ADR & 0x80);	// Wait for BUSY->'0' (data ready)
	
	// Set In Packet ready bit, indicating fresh data on FIFO1
	UWRITE_BYTE(EINCSRL, rbInINPRDY);
}

}

From MCU C8051F320 datasheet:

Hardware will automatically reset INPRDY (EINCSRL.0) to ‘0’ when a packet slot is open in the endpoint
FIFO. Note that if double buffering is enabled for the target endpoint, it is possible for firmware to load two
packets into the IN FIFO at a time. In this case, hardware will reset INPRDY to ‘0’ immediately after
firmware loads the first packet into the FIFO and sets INPRDY to ‘1’. An interrupt will not be generated in this
case; an interrupt will only be generated when a data packet is transmitted.
The ISO Update feature ensures that any data packet written to the endpoint FIFO will not
be transmitted during the current frame; the packet will only be sent after a SOF signal has been received.

Ok checked 16 packets , same result

nvUSB.lib: Current Frame number=873899931
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 1 completed. Read 32 bytes.
nvUSB.lib: Current Frame number=873899947
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873899963
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 3 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873899979
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 4 completed. Read 0 bytes.

The question is why only first transfer working OK and others 2,3,4 return with error
USBD_STATUS_ISO_NOT_ACCESSED_BY_HW in isochPacketDescriptors[j].Status

nvUSB.lib: Current Frame number=873899931
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 1 completed. Read 32 bytes. <------------------------------------------------------
nvUSB.lib: Current Frame number=873899947
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873899963
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 3 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873899979
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 4 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873900000
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 1 completed. Read 32 bytes. <------------------------------------------------
nvUSB.lib: Current Frame number=873900021
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 2 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873900042
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 3 completed. Read 0 bytes.
nvUSB.lib: Current Frame number=873900063
nvUSB.lib: bRead() Requested 384 bytes in 16 packets per transfer.
nvUSB.lib: bRead() Transfer 4 completed. Read 0 bytes.

So, are you unconditionally setting ContinueStream to FALSE? I don’t understand what you were trying to accomplish with the manipulation at the end of your function. Just set that to FALSE, always.

Sending FALSE always - same behavior