6 Replies Latest reply on Sep 14, 2018 3:24 AM by keaj

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

    chuck_3457901

      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;

      }