This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Non-blocking ReadUSBPacket() loosing data

Hello,


I've been using TivaWare_C_Series-2.0.1.11577 with TM4C123GH6ZRB. I created a USB bulk device and it talked to  PC using TI bulk device driver. I modified the usb_bulk_example and it can send/receive data to/from the board.

But I found when the message is relatively large (in this case, more than 512 bytes), if the PC uses ReadUSBPacket(...) with a timeout value (50 ms here) to receive data, it will lose data. For example, using

dwError = ReadUSBPacket(hUSB, szBuffer, 64, &ulRead, 50, NULL);

it may lose a block of 64 bytes from time to time. If the buffer size is changed to 128 bytes, it will lose blocks of 128 bytes. If I change the buffer size to way bigger than the message size, it will lose a whole message from time to time. If I make it a blocking read (changing 50 to INFINITE), it won't lose any data.

On the PC side, I added up the ulRead numbers. On the board, I counted the bytes sent out in RxHandler(). They didn't match. The data rate was very low - it could be just a message a second.

Any help is greatly appreciated!

Yanli

  • Hi Yanli,

    I'm very sorry about not responding your question sooner. 

    I understand that your host application is loosing data when using timeout in the API call. Can you tell us when your windows application calls this API, and how it handles the situation when the API returns error code due to timeout.

    I will try to reproduce it with our sample code, and let you know once I have any new information.

    Angela

  • Hi Angela,

    Thanks for your response. When the API call returns WAIT_TIMEOUT, the program doesn't do anything. It just comes back later with another call to ReadUSBPacket().

    Yanli

  • Yanli,

    Can you make sure that your program will call ReadUSBPacket() immediately after the previous call returns WAIT_TIMEOUT, this guarantees that packets are read out of USB as fast as it receives, and prevent any packets loss.  Basically you will need to call ReadUSBPacket() in a while(1) loop as long as it returns WAIT_TIMEOUT, this is basically the same as the blocking call. You can add a counter in the loop so it doesn't wait forever.

    Let me know if this helps.

    Angela


  • The reason non-blocking ReadUSBPacket() was used was that the program needed to do other things besides reading data from the USB pipe. I did make sure the intervals between the calls were short enough to read out all the data that the board put into the USB pipe.

    Below is the definition of ReadUSBPacket() in the lmusbdll project that comes with TivaWare. It looks like when WaitForMultipleObjects() returns WAIT_TIMEOUT, WinUsb_AbortPipe() is called and WAIT_TIMEOUT is returned to the user. What does WinUsb_AbortPipe() do to the USB buffering?


    Thanks for your help!

    Yanli


    ReadUSBPacket(LMUSB_HANDLE hUSB, unsigned char *pcBuffer, unsigned long ulSize,
                  unsigned long *pulRead, unsigned long ulTimeoutmS, HANDLE hBreak)
    {
        BOOL bResult;
        DWORD dwError;
        OVERLAPPED sOverlapped;
        HANDLE phSignals[2];
        tDeviceInfoWinUSB *psDevInfo = (tDeviceInfoWinUSB *)hUSB;

        //
        // Check for bad parameters.
        //
        if(!hUSB || !pcBuffer || !pulRead || !ulSize)
        {
            return(ERROR_INVALID_PARAMETER);
        }

        //
        // Tell WinUSB how to signal us when reads are completed (if blocking)
        //
        sOverlapped.hEvent = psDevInfo->hReadEvent;
        sOverlapped.Offset = 0;
        sOverlapped.OffsetHigh = 0;

        //
        // Perform the read.
        //
        bResult = WinUsb_ReadPipe(psDevInfo->winUSBHandle,
                                  psDevInfo->bulkInPipe,
                                  pcBuffer,
                                  ulSize,
                                  pulRead,
                                  &sOverlapped);

        //
        // A good return code indicates success regardless of whether we performed
        // a blocking or non-blocking read.
        //
        if(bResult)
        {
            dwError =  ERROR_SUCCESS;
        }
        else
        {
            //
            // An error occurred or the read will complete asynchronously.
            // Which is it?
            //
            dwError = GetLastError();

            //
            // Check for error cases other than the one we expect.
            //
            if(dwError == ERROR_IO_PENDING)
            {
                //
                // The IO is pending so wait for it to complete or for a
                // timeout to occur.
                //
                phSignals[0] = psDevInfo->hReadEvent;
                phSignals[1] = hBreak;
                dwError = WaitForMultipleObjects(hBreak ? 2 : 1, phSignals, FALSE,
                                                 ulTimeoutmS);

                //
                // At this stage, one of three things could have occurred.
                // Either we read a packet or we timed out or the break
                // signal was detected.  Which was it?
                //
                if(dwError == WAIT_OBJECT_0)
                {
                    //
                    // The overlapped IO request completed so check to see how
                    // many bytes we got.
                    //
                    bResult = GetOverlappedResult(psDevInfo->deviceHandle,
                                                  &sOverlapped,
                                                  pulRead, FALSE);
                    if(bResult)
                    {
                        dwError = ERROR_SUCCESS;
                    }
                    else
                    {
                        //
                        // Something went wrong.  Return the Windows error code.
                        //
                        dwError = GetLastError();
                    }
                }
                else
                {
                    //
                    // Something went wrong - abort the transfer
                    //
                    WinUsb_AbortPipe(psDevInfo->winUSBHandle,
                                     psDevInfo->bulkInPipe);

                    //
                    // Was the break event signalled?
                    //
                    if(dwError  == (WAIT_OBJECT_0 + 1))
                    {
                        //
                        // The break event was signalled - abort the read and return.
                        //
                        dwError = WAIT_OBJECT_0;
                    }
                }
            }
        }

        //
        // Pass the result back to the caller.
        //
        return(dwError);
    }

  • StellarisAngela said:
    Basically you will need to call ReadUSBPacket() in a while(1) loop as long as it returns WAIT_TIMEOUT, this is basically the same as the blocking call. You can add a counter in the loop so it doesn't wait forever.

    Unfortunately, this is a bad manner to apply timeout on WinUSB (**1), because the timout operation causes the data drop on WinUSB driver. Timeout cancels "read" operation (ie. bulk IN transfer) on PC host controller. This cancel operation even discards completed bulk IN transfer, when the transfer completion occurs almost simultaneously at timeout. And then, it results in data drop. Timeout is dangerous, as it sometimes causes even BSOD on WinUSB.

    (**1) ReadUSBPacket() in TivaWare tools\usb_bulk_example is implemented on WinUSB, either directly or over libusb-Win32.

    The principles are,
    a) Don’t use timeout, if possible.
    b) If timeout would be really required, set the timeout value long enough, so that timeout never occurs in normal operation.

    Take examples in two popular scenarios.
    A) Query - Reply communication
    In this scenario, host PC sends a query (command) to a device first, and then, the device returns a reply. Host knows the timing when the reply comes. You may set the timeout at ReadUSBPacket(), to detect no response from the device. You should estimate the max response time to return the reply on the device. The timeout value is set to more than this max response time.

    B) Asynchronous transfer from device to host
    In this scenario, device sends data without any query from host. As the host doesn’t knows the timing when data comes from the device, no timtout should apply. ReadUSBPacket() (or WinUsb_ReadPipe()) is called in a separate thread without timeout, so that it doesn’t block the main thread. Just when the PC application exits, the thread terminates and the WinUsb_ReadPipe() is cancelled.

    Tsuneo

  • I'm wondering if WinUsb_AbortPipe() is not called when timeout occurs, what would happen to the USB pipe. Can you call ReadUSBPacket() again later?

    Yanli

  • Yanli Hu said:
    I'm wondering if WinUsb_AbortPipe() is not called when timeout occurs, what would happen to the USB pipe.

    When timeout occurs, WinUSB driver (winusb.sys) calls "Abort Pipe" internally, after canceling pended "Bulk or Interrupt Transfer". You can confirm it using software sniffer on your PC. Here is a typical sniffer log of bulk IN transfer with 100ms timeout.
    http://www.microchip.com/forums/download.axd?file=0;639589&filename=trace2.png

    Yanli Hu said:
    Can you call ReadUSBPacket() again later?

    WinUsb_ResetPipe() may recover the pipe object on WinUSB, after timeout occurs.
    WinUsb_ResetPipe() puts Clear_Feature( ENDPOINT_HALT ) to the device. Receiving this Request, your firmware should abort current "transfer" on the endpoint, if it were still going on.

    Tsuneo

  • Yanli,

    Have you had a chance to try creating another thread to read packet without timeout? After reading through the code, as Tsuneo said, It is likely data will get lost when calling the API with timeout.

    I am not  familiar with WinUSB, I will have to dig into deeper to understand how it works.

    Angela

  • To close out this thread, the issue was solved by using multiple threads and blocking ReadUSBPacket().

     

    Katie Vinnedge