Handling USB packet loss

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
david-given
Level 4
Level 4
10 sign-ins 5 sign-ins First solution authored

Question: is there anything specific in my code that I need to do to handle USB packet loss in order to ensure that retransmission happens?

Context:

I'm sending ~800kB/s via bulk transfers from a PSoC5LP device to the host. There are occasional problems where transfers fail in the middle in weird ways. I managed to duplicate this myself, and it looks like the device is waiting for a packet acknowledgement which never shows up. I worked on this and it went away, but now I'm getting reports from users that it's come back. I now wonder whether this might be caused by bad cables, and I hadn't fixed it, I just managed to swap cables without noticing.

Are retries on errors handled for me by the USBFS library, or is this something I'm expected to deal with myself? If the latter, can anyone point me at sample code on how to do this correctly?

Thanks!

0 Likes
1 Solution

I had some problems with the USBUart error conditions, when transmitting data to the host, causing lockups.

I solved it by borrowing the PutString code from the library and modifying it to handle an error condition.

In this project, I am using FreeRTOS, so I was able to do a pause in the code to allow for processing.

Note the exit if we have delayed twice with no success.  This usually means the USB was disconnected,

and you have to give up. The code provided by Cypress simply locks the USB and you can never recover.

This code at least attempts to send the data, and gives up (maybe not gracefully, but in my case it was acceptable).

You could modify the routine to return an error code to the calling routine should you need to.

*Edit* Also, if you are using a variant of sprintf() or printf(), make sure your heap size is set to at least 2k bytes, and the stack size set to at least 4k bytes (in the system tab of the .cydwwr Design Wide Resources  editor)  Insufficient stack size or heap size can cause issues like this.

// the following uint8 is borrowed from USBUART_cdc.c

// and only functions if 1 uart is being used, which in our

// case is always true

static uint8 USBUART_activeCom = 0u;

int16 delayCounter;

void myUSBUART_PutString(const char8 string[])

{

    uint16 strLength;

    uint16 sendLength;

    uint16 bufIndex = 0u;

    uint8  epNumber = USBUART_cdcDataInEp[USBUART_activeCom];

    /* Get length string length (it is terminated with zero). */

    strLength = strlen(string);

    do

    {

        /* Limit length to maximum packet size of endpoint. */

        sendLength = (strLength > USBUART_EP[epNumber].bufferSize) ?

                                  USBUART_EP[epNumber].bufferSize : strLength;

        /* Load IN endpoint and expose it to host. */

        USBUART_LoadInEP(epNumber, (const uint8 *)&string[bufIndex], sendLength);

        strLength -= sendLength;

        /* If more data are present to send or full packet was sent */

        if ((strLength > 0u) || (sendLength == USBUART_EP[epNumber].bufferSize))

        {

            bufIndex += sendLength;

            delayCounter=0;

            /* Wait until host read data from IN endpoint buffer. */

            while (USBUART_IN_BUFFER_FULL == USBUART_EP[epNumber].apiEpState)

            {

                // don't wait very long here.  allow other code to run in the calling task

                    vTaskDelay(pdMS_TO_TICKS(10));// delay until reconnect

                    if (delayCounter++>2)

                        break;

            }

            /* If last packet is exactly maximum packet size, it shall be followed

            * by a zero-length packet to assure the end of segment is properly

            * identified by the terminal.

            */

            if (0u == strLength)

            {

                USBUART_LoadInEP(epNumber, NULL, 0u);

            }

        }

    }

    while (strLength > 0u);

}

View solution in original post

0 Likes
3 Replies
lock attach
Attachments are accessible only for community members.
Ekta_N
Moderator
Moderator
Moderator
750 replies posted First like given 250 solutions authored

Hello David,

In case of IN transfers, the transaction is initiated by the Host by sending an IN token packet, the targeted device responds by sending one or more data packets and the host responds with a handshake packet (ACK/ NAK, etc.). If the device responds with NAK to show that it is not ready to send data when the host makes the request. The host continues to retry and the device responds with a data packet when it is ready. The host then acknowledges the receipt of the data with an ACK handshake.

Please refer to the USB 101 Section 9.2 for more details on this: https://www.cypress.com/file/134171/download

In Full-Speed (PSoC 5LP) devices, the maximum cable length is 5 meters. Please ensure that your cable is within the USB spec.

You can refer to the USBFS Bulk Wraparound code example for the implementation of Bulk transfers in case of PSoC 5LP. I am attaching the code example with this response.

Let me know it this helps. Please attach the project so that we can have a look at it, this will help us get a better insight of the issue.

Best Regards

Ekta

0 Likes

Thanks, but that doesn't actually answer my question! Is there anything I need to do in my code to deal with retries on error, or is this all taken care of for me by the USBFS library? Your sample project doesn't contain any error handling.

I can't attach my project because it relies on files outside the project directory, but it's in github here: fluxengine/main.c at master · davidgiven/fluxengine · GitHub

0 Likes

I had some problems with the USBUart error conditions, when transmitting data to the host, causing lockups.

I solved it by borrowing the PutString code from the library and modifying it to handle an error condition.

In this project, I am using FreeRTOS, so I was able to do a pause in the code to allow for processing.

Note the exit if we have delayed twice with no success.  This usually means the USB was disconnected,

and you have to give up. The code provided by Cypress simply locks the USB and you can never recover.

This code at least attempts to send the data, and gives up (maybe not gracefully, but in my case it was acceptable).

You could modify the routine to return an error code to the calling routine should you need to.

*Edit* Also, if you are using a variant of sprintf() or printf(), make sure your heap size is set to at least 2k bytes, and the stack size set to at least 4k bytes (in the system tab of the .cydwwr Design Wide Resources  editor)  Insufficient stack size or heap size can cause issues like this.

// the following uint8 is borrowed from USBUART_cdc.c

// and only functions if 1 uart is being used, which in our

// case is always true

static uint8 USBUART_activeCom = 0u;

int16 delayCounter;

void myUSBUART_PutString(const char8 string[])

{

    uint16 strLength;

    uint16 sendLength;

    uint16 bufIndex = 0u;

    uint8  epNumber = USBUART_cdcDataInEp[USBUART_activeCom];

    /* Get length string length (it is terminated with zero). */

    strLength = strlen(string);

    do

    {

        /* Limit length to maximum packet size of endpoint. */

        sendLength = (strLength > USBUART_EP[epNumber].bufferSize) ?

                                  USBUART_EP[epNumber].bufferSize : strLength;

        /* Load IN endpoint and expose it to host. */

        USBUART_LoadInEP(epNumber, (const uint8 *)&string[bufIndex], sendLength);

        strLength -= sendLength;

        /* If more data are present to send or full packet was sent */

        if ((strLength > 0u) || (sendLength == USBUART_EP[epNumber].bufferSize))

        {

            bufIndex += sendLength;

            delayCounter=0;

            /* Wait until host read data from IN endpoint buffer. */

            while (USBUART_IN_BUFFER_FULL == USBUART_EP[epNumber].apiEpState)

            {

                // don't wait very long here.  allow other code to run in the calling task

                    vTaskDelay(pdMS_TO_TICKS(10));// delay until reconnect

                    if (delayCounter++>2)

                        break;

            }

            /* If last packet is exactly maximum packet size, it shall be followed

            * by a zero-length packet to assure the end of segment is properly

            * identified by the terminal.

            */

            if (0u == strLength)

            {

                USBUART_LoadInEP(epNumber, NULL, 0u);

            }

        }

    }

    while (strLength > 0u);

}

0 Likes