Restart USB transfers from CX3 to host after CyU3PDmaMultiChannelCommitBuffer failure (without restarting host software)

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

cross mob
Anonymous
Not applicable

Hi,

I am working with a CX3+OV5640 device that is operating as a UVC peripheral. My code is based on the latest version of the UVC app note (don't remember the number, but I updated it within the last month). My host OS is Linux and I am using v4l2 to read frames from the CX3. In general things work just fine. However, there are some conditions (especially when running a 4.9.x kernel) when they will occasionally fail after 5-20 minutes, whereas on a 4.14.x kernel I have run for 3+ days continuously without problems. Bisecting the two kernels is a giant pain. So, my goal is to accept the occasional failure and transparently recover from it (flagging an error, but not requiring the v4l2 application to restart anything).

I have tried various things and from what I see on a logic analyzer, I can get the producer side of the firmware to start back up after the CX3 HW state machine re-synchronizes with the frame start signal coming from the OV5640. However, I stop seeing consumer-side DMA completion callbacks once a failure occurs, and I don't see data make their way back into the host, either. I have the code instrumented to raise an IO on entry to the producer side of the DMA callback and drop it on exit, raise another IO on entry to the consumer side of the DMA callback and drop it on exit, and a third IO set to generate a rising edge on error (either timer-expired callback or commit buffer failure) and then it gets cleared as the last thing to happen in my adapted restart function. There's also a fourth IO that's programmed to toggle on each frame.

I've attached a screenshot from the logic analyzer showing this behavior. In it you can see normal operation for a few frames, then a failure. After the first failure, the producer side starts back up, but no activity occurs on the consumer side. This triggers another error and it all repeats. This particular capture was also monitored on the USB host via usbmon and wireshark. The capture there seems to show that the host side is issuing IN URBs that just never get responses from the CX3. I've attached the pcap file for that starting a few packets before the end of the last successful frame and stopping after the failure (it was running for a couple of minutes post-failure, there is just nothing happening on the USB interface, so packets stop). From this it also looks like the last packet into the host is short (21500 vs. 24572 bytes). Insight into the reason behind this would also be wonderful.

I have (adapted for the CX3 and) implemented the proposed application restart function as found in Error in example UVC_AN75779.​ I have tried calling this restart function both from within the DMA callback in response to CyU3PDmaMultiChannelCommitBuffer returning an error (it's always -71 for me). In this version of my setup I have avoided flagging the error to the main application thread. I've also tried flagging the error to the application thread and had the application thread run the restart function in response to the error. In other words, I've tried handling the error in two places and neither seems to work better (or worse) than the other.

I don't mind if the select call on my v4l2 file descriptor times out, my capture loop will just select on it again and go on with things.

Is there something I am missing or something else I need to do?

Thanks in advance!

For reference, my restart function is replicated below:

CyU3PReturnStatus_t CyFxUvcApplnRestart()

{

    CyU3PReturnStatus_t apiRetStatus;

    // cancel the frame timer

    CyU3PTimerStop(&UvcTimer);

    /* Disable the GPIF state machine. */

    CyU3PGpifDisable (CyFalse);

    /* Abort and destroy the video streaming channel */

    CyU3PDmaMultiChannelReset(&glChHandleUVCStream);

    CyU3PThreadSleep(25);

    /* Flush the endpoint memory */

    CyU3PUsbFlushEp(EP_UVC_VIDEO);

    CyU3PBusyWait(200);

    CyU3PUsbFlushEp(EP_UVC_VIDEO);

    apiRetStatus = CyU3PDmaMultiChannelReset (&glChHandleUVCStream);

    if (apiRetStatus != CY_U3P_SUCCESS)

    {

        log_err("\n\rAplnStrt:ChannelReset Err = 0x%x\n", apiRetStatus);                                                                                                                                                                                                        

        return apiRetStatus;

    }

    apiRetStatus = CyU3PDmaMultiChannelSetXfer (&glChHandleUVCStream, 0, 0);

    if (apiRetStatus != CY_U3P_SUCCESS)

    {  

        /* Error handling */

        log_err("DMA Channel Set Transfer Failed, Error Code = %d\n", apiRetStatus);

        return apiRetStatus;

    }

    /* Start with frame ID 0. */

    glUVCHeader[1] &= ~CY_FX_UVC_HEADER_FRAME_ID;

    /* Start the state machine from the designated start state. */

    apiRetStatus = CyU3PGpifSMStart (CX3_START, CX3_ALPHA_START);

    if (apiRetStatus != CY_U3P_SUCCESS)

    {  

        /* Error Handling */

        log_err("Starting GPIF state machine failed, Error Code = %d\n", apiRetStatus);

        return apiRetStatus;

    }

    // restart frame timer

    CyU3PTimerModify(&UvcTimer, glFrameTimerPeriod, 0);

    CyU3PTimerStart(&UvcTimer);

    // clear error gpio

    CyU3PGpioSimpleSetValue(ERROR_GPIO, CyFalse);

    return 0;

}

0 Likes
6 Replies
Keerthy_V
Moderator
Moderator
Moderator
First like given 250 sign-ins 50 solutions authored

Hi,

1)Can you please provide the resolution and the frame rate that you are trying to stream?

2)Are you seeing any USB endpoint event when the failure happens? Please register for the endpoint event callback using the API CyU3PUsbRegisterEpEvtCallback.

3)Please try increasing the DMA buffering. You can increase the DMA buffer space by modifying the linker file.

0 Likes
Anonymous
Not applicable

1) 1280x720 YUYV at 60 fps

2) I see a CYU3P_USBEP_SS_RESET_EVT

3) I do not want to do this. I WANT the device to fail currently. I cannot ever prove that it won't fail in actual operation, but I can show that the code recovers cleanly when a failure occurs. I've been reducing buffer count and size in order to make things fail more often / more quickly.

The firmware docs say that in the case of the RESET event, a stall recovery is required. I haven't found information about that. Is there a way to do this without the host application having to do anything?

0 Likes
Anonymous
Not applicable

I just added a pair of calls to CyU3PUsbStall (called as CyU3PUsbStall(ep, CyTrue, CyTrue); CyU3PUsbWait(200); CyU3PUsbStall(ep, CyFalse, CyTrue); ) and currently have one instance of the code recovering transparently to the application. Can you confirm that this is the proper way to clean up after the RESET event?

After running a bit longer, it seems that this does not always work. In some cases I am also seeing CYU3P_USBEP_SS_SEQERR_EVT.

0 Likes
Anonymous
Not applicable

On a (maybe) related note, I see a decent number of CYU3P_USBEP_SS_RETRY_EVT. Is this normal?

0 Likes
Anonymous
Not applicable

After running this a good number more times, it seems that it'll work the first 3 times, but then on the 4th it fails in the same manner as shown in my original logic analyzer screenshot (i.e. the mipi side starts back up, but the usb side does not). In rare cases it will work 4 times and then fail on the 5th (so presumably there's some timing-related issue here?).

Additionally, I am now always getting the CYU3P_USBEP_SS_RESET_EVT event. Following that I see the multi-channel commit buffer failure. During the call to my restart function, I see the CYU3P_USBEP_SS_SEQERR_EVT event. Then, when things work, it all goes back to normal (i.e. sporadic CYU3P_USBEP_SS_RETRY_EVT events, but no other USB endpoint events). When things fail to work, I see repeated multi-channel commit buffer failures and no more USB endpoint events.

0 Likes
Keerthy_V
Moderator
Moderator
Moderator
First like given 250 sign-ins 50 solutions authored

Hi,

You need to perform an endpoint specific recovery when the CYU3P_USBEP_SS_RESET_EVT event is received. The recommended recovery procedure is to stall the endpoint and then stop and restart the DMA path when the CLEAR_FEATURE request is received. Please refer to GpifToUsb example project for the implementation of this sequence.

if (evtype == CYU3P_USBEP_SS_RESET_EVT)

    {

          /*Stall the endpoint on which error  occurred*/

            CyU3PUsbStall (epnum, CyTrue, CyFalse);

    }

if ((bTarget == CY_U3P_USB_TARGET_ENDPT) && (bRequest == CY_U3P_USB_SC_CLEAR_FEATURE)

                && (wValue == CY_U3P_USBX_FS_EP_HALT))

        {

                    CyU3PUsbSetEpNak (epnum, CyTrue);

                    CyU3PBusyWait (125);

                    CyU3PDmaChannelReset (&glDmaChHandle);

                    CyU3PUsbFlushEp(epnum);

                    CyU3PUsbResetEp (epnum);

                    CyU3PDmaChannelSetXfer (&glDmaChHandle, 0);

                    CyU3PUsbStall (wIndex, CyFalse, CyTrue);

                    CyU3PUsbSetEpNak (epnum, CyFalse);

                    isHandled = CyTrue;

                    CyU3PUsbAckSetup ();

         }

0 Likes