6 Replies Latest reply on Oct 30, 2019 12:06 AM by YatheeshK_36

    .NET CyUSBDevice Recovery

      Hey all,

       

      I'm working on making my wrapper around the the CyUSB interface very robust, including being thread and process safe. I've gotten the device to recove in most of my uses cases via code, but there are still some important edge cases that currently require manually unplugging and replugging in the device to make it reset, or else needing to restart an application - which is undesirable.

       

      To simulate different scenarios where things can go awry I'm using the CyUSBDevice reset and reconnect routines. In one of my tests I launch a separate thread that writes and reads a bunch of bulk data.What I want to happen is for the thread performing these I/O transactions to fail when I reset/reconnect/disconnect the device (whether through a false return value or exception). Sometimes this works, but other times it does not. Rather what happens is the CyUSBEndPoint gets stuck in the WaitForIO routine underneath the XferData function and hangs there, never returning! The only way to recover at this point is to kill the application..


      Note that i have set the End Point Timeouts to 1sec in these tests - so the I/O if failing to timeout.

       

      Please advise on how to properly interact with the CyUSBDevice and the Bulk End points so that this does not occur. I am using a global mutex to provide thread and process safe access to the device when reading/writing - but there is always the potential for *something* to take down the device, even if just physically unplugging and replugging it back in. I should be able to recover without the CyUSB getting into a deadlock.

       

      Thanks,
      Ryan

        • 1. Re: .NET CyUSBDevice Recovery
          SrinathS_16

          Hello Ryan,

           

          - Please post the code snippet of the host application if possible

          - Have you tried using the Cypress USB Control Center with a large bunch of data to be transferred during which the device is removed? I tested this on the FX3 Super Speed Explorer Kit and found that the XferData() call either returns success or it fails but does not get stuck.

          - Also, kindly, let me know if you have implemented the DeviceRemoved() event handler in your application.

           

          Best regards,

          Srinath S

          • 2. Re: .NET CyUSBDevice Recovery

            Hi Srinath,

             

            Below is the common routine used by my code for sending bulk requests (using a request-response model), let me know if you see anything odd. Note that currently the invoker will supply a ResponseBuffer of the appropriate length. If you have a good example of how to figure out how large the response is dynamically that would be helpful:

            ----------------------------------------------------------------------------------------------

             

            Public Overloads Sub USBIO(ByVal CommandName As String, ByVal CommandBuffer() As Byte, ByVal ResponseBuffer() As Byte, Optional ByVal TimeOutInSec As Single = 1)

                    ThrowIfDisposed()

             

                    'Check if Driver Started

                    If Not Enabled Then Return

             

                    Dim commandBufferHandle As GCHandle = Nothing

                    Dim responseBufferHandle As GCHandle = Nothing

             

                    Try

                        _deviceMutex.WaitOne() ' Only one thread in one process may work with this FIC2USB Device at a time

                    Catch ex As AbandonedMutexException

                        ' TO DO: Adding diagnostics to note that the mutex was abandoned by another process (such as from a crash)

                        ' NOTE: Even if this exception occurs, we still successfully acquired the mutex

                    End Try

             

                    Try

                        ThrowIfDisposed()

             

                        ' BEWARE OF CATASTROPHIC CYUSB BUG: We must pin the command and response buffers prior to passing them off to the unmanaged code,

                        ' lest we risk a fatal excepion that will crash the app (regardless of any try...catch block). This bug can be seen when doing

                        ' a lot of I/O in very tight loops.

                        commandBufferHandle = GCHandle.Alloc(CommandBuffer, GCHandleType.Pinned)

                        responseBufferHandle = GCHandle.Alloc(ResponseBuffer, GCHandleType.Pinned)

             

                        'Set Timeout (of input buffer only)

                        Me.CyFX2Device.BulkInEndPt.TimeOut = CUInt(TimeOutInSec * 1000)

             

                        'Send Command

                        If Me.CyFX2Device.BulkOutEndPt.XferData(CommandBuffer, CommandBuffer.Length) = False Then

                            Throw New System.IO.IOException(String.Format("Failed to XferData to BulkOutEndPt 0x{0}", Hex(CyFX2Device.BulkOutEndPt.Address)))

                        End If

             

                        'Read Response

                        If Me.CyFX2Device.BulkInEndPt.XferData(ResponseBuffer, ResponseBuffer.Length) = False Then

                            Throw New System.IO.IOException(String.Format("Failed to XferData from BulkInEndPt 0x{0}", Hex(CyFX2Device.BulkInEndPt.Address)))

                        End If

             

                    Catch ex As Exception

                        Me.CyFX2Device.BulkOutEndPt.Reset()

                        Me.CyFX2Device.BulkInEndPt.Reset()

                        LastError = ex

                        Throw ex

                    Finally

                        _deviceMutex.ReleaseMutex()

                        If Not IsNothing(commandBufferHandle) Then commandBufferHandle.Free()

                        If Not IsNothing(responseBufferHandle) Then responseBufferHandle.Free()

                    End Try

                End Sub

            • 3. Re: .NET CyUSBDevice Recovery

              Also, here is the code that handles the device attached/detached events (both call the same function). This is executed under a singleton manager class that wraps the CyUSBDeviceList instance for the process. It will in turn create wrappers for the individual CyUSBDevices that we care about. If you have any suggestions for improvement here let me know.
              ------------------------------------------------------------------

              Private Sub UpdateDeviceList()

                      ' Go through the CyUSB.USBDeviceList to identify anything that has been added/removed

               

                      _deviceListMutex.WaitOne()

                      Try

                          ' First check for new devices

                          For Each cyDev As CyUSB.CyUSBDevice In mCyUSBDevices

               

                              Dim devMutex = GetMutex(cyDev)

               

                              'Try

                              Dim target = myDeviceList.Values.FirstOrDefault(Function(ByVal wrapper As USBDeviceWrapper) wrapper.USBAddress = cyDev.USBAddress)

               

                              If IsNothing(target) Then

               

                                  If (cyDev.VendorID = MY_VID) Then

                                      If (cyDev.ProductID >= MY_PID_LOW) And (cyDev.ProductID <= MY_PID_HIGH) Then

                                          Dim newDev As New USBDeviceWrapper(cyDev, devMutex)

                                          myDeviceList.Add(newDev.USBAddress, newDev)

                                          NotifyDeviceAttached(newDev)

                                      End If

                                  End If

               

               

                              ElseIf target.VendorID <> cyDev.VendorID OrElse target.ProductID <> cyDev.ProductID Then

               

                                  myDeviceList.Remove(target.USBAddress)

               

                                  target.Dispose()

                                  NotifyDeviceRemoved(cyDev.USBAddress)

               

                                  If (cyDev.VendorID = MY_VID) Then

                                      If (cyDev.ProductID >= MY_PID_LOW) And (cyDev.ProductID <= MY_PID_HIGH) Then

                                          Dim newDev As New USBDeviceWrapper(cyDev, devMutex)

                                          myDeviceList.Add(newDev.USBAddress, newDev)

                                          NotifyDeviceAttached(newDev)

                                      End If

                                  End If

               

                              ElseIf target.CyFX2Device.DeviceHandle <> target.CyFX2Device.DeviceHandle Then

                                  ' The device was reconnected (?)

                                  target.CyFX2Device = cyDev

                              End If

                          Next

               

                          ' Second check for removed devices

                          Dim wrapperList = myDeviceList.Values.ToArray()

                          For Each curr As usbFIC2USB In wrapperList

                              Dim devFound As Boolean = False

                              Dim devIndex As Integer = 0

               

                              For devIndex = 0 To mCyUSBDevices.Count - 1

                                  If curr.USBAddress = mCyUSBDevices(devIndex).USBAddress Then

                                      devFound = True

                                      Exit For

                                  End If

                              Next

               

                              If devFound Then Continue For

               

                              ' The wrapper did not have a corresponding cyUSBDevice, so dispose of it

                              myDeviceList.Remove(curr.USBAddress)

                              NotifyDeviceRemoved(curr.USBAddress)

                              curr.Dispose()

                          Next

               

                      Catch ex As Exception

              #If DEBUG Then

                          Debugger.Launch()

              #End If

                          Throw ex

                      Finally

                          _deviceListMutex.ReleaseMutex()

                      End Try

               

                  End Sub

              • 4. Re: .NET CyUSBDevice Recovery

                Also, after I fixed a small bug in my NotifyDeviceRemoved function that was preventing listeners from being notified that the device had been removed, the problem appears to have gone away. However, this shouldn't make any difference. Even if other code incorrectly tries to access a disposed CyUSBDevice object (since they weren't notified about it being removed), any requests should still ultimately return with either a false return value or by means of an exception and not hang. However, perhaps this will help focus the investigation into fixing this off-case.

                 

                Is there perhaps some use of a WeakReference under the covers that is being checked for disposal purposes?

                 

                EDIT: I confirmed that I could re-create the problem by commenting out the few lines of code above that are responsible for notifying listeners that a device was removed and for removing the device from the local list of devices and disposing the wrapper object. So likely a reference counting issue going on here.

                • 5. Re: .NET CyUSBDevice Recovery

                  The CyUSB Device hanging issue continues to intermittently pop up. This time I'm seeing it hang after a Bulk XFER fails and I attempt to Reset the BulkInEndPt. The reset command hangs.

                  • 6. Re: .NET CyUSBDevice Recovery
                    YatheeshK_36

                    Hello,

                     

                    Please refer to the USB control center application present in the FX3 SDK under C:\Program Files (x86)\Cypress\EZ-USB FX3 SDK\1.3\application\c_sharp\controlcenter

                     

                    This should give a clear idea in which default application is implemented.

                     

                    Best Regards,

                    Yatheesh