6 Replies Latest reply on Apr 9, 2010 3:42 PM by andrew.sobotka

    unexpected WaitForXfer() behavior

    andrew.macdonald
              Hi,   
         
      I'm writing an application based on the Screamer example code and I'm seeing strange behavior from the WaitForXfer method. At least, I think it's strange based on how I've seen WaitForXfer used in the code examples. Also, the documentation doesn't give much in the way of details.   
         
      In my program, I'm trying to read a continuous stream of data from a bulk endpoint using the queued overlapped-transfer methodology provided in the Screamer example code (see the code's XferThread, LockNLoad and XferData methods). In fact, I haven't changed much in that section of the code, except for adding a wait loop at the WaitForXfer call and writing the incoming data to file every one cycle through the queue.   
         
      My problem is that WaitForXfer always returns FALSE, meaning it has timed-out waiting for the OS to signal that the requested data has arrived. This is wrong, because I can see in USBlyzer (a sniffer program) that the requests are going out on the USB (from the preceding BeginDataXfer calls) and that the expected data is arriving from the device. My program sits in a loop, calling WaitForXfer multiple times and waiting for it to return TRUE, so that the program might proceed to call FinishDataXfer and write the data to a file. Even though the data has already come in, WaitForXfer never indicates its presence (seems to me that it should), and so my program stays in the wait loop. I'm giving WaitForXfer a reasonable timeout of 500ms. Since CyUSB is a black-box, I'm not sure how to troubleshoot this any further.   
         
      Is my understanding of WaitForXfer's intended behavior correct?   
      Can anyone provide some insight into what the problem might be?   
         
      I'm using Windows XP and SuiteUSB 2.0.   
         
      Thanks   
         
        • 1. Re: unexpected WaitForXfer() behavior
          andrew.macdonald
                  ...I should also mention that the hEvent handle is NOT mistakenly modified anywhere in the code before being passed to WaitForXfer. It is initialized with CyUSB's PInvoke.CreateEvent(0,0,0,0) and the same hEvent value is passed to WaitForXfer. I wondered if this might be due to a naughty C# garbage-collector moving something that hEvent points to, but that shouldn't happen, since all these operations are fixed{}. Again, this part isn't my own code. I haven't modified this section from the original Screamer example, so it should be fine, right?   
          • 2. Re: unexpected WaitForXfer() behavior
            andrew.sobotka
                    WaitForXfer is a wrapper for WaitForSingleObject (    http://msdn.microsoft.com/en-us/library/ms687032.aspx ). The example code shows them using PInvoke to call that very function.   
               
            If you're interested, I suggest browsing MSDN's documentation for the OVERLAPPED structure (    http://msdn.microsoft.com/en-u...y/ms684342(VS.85).aspx ), and CreateEvent and its cousins SetEvent and ResetEvent (    http://msdn.microsoft.com/en-u...y/ms682396(VS.85).aspx ).   
               
            When the data is being transferred, the driver notifies the user-mode application by setting (or "signaling") the hEvent member of the overlapped object that was passed to the driver from BeginDataXfer(). The user-mode app can wait for the hEvent to become signaled using WaitForXfer, or the win32 function WaitForSingleObject, or the win32 macro GetOverlappedResult. Once signaled, a call to FinishDataXfer releases the buffers that were needed by the driver.   
               
            If you do not call ResetEvent to clear the hEvent back to an unsignaled state, you are going to get unexpected behavior. I imagine that the first wave of transfers would go fine, as the hEvent members are one-by-one signaled, but once you start trying to re-use previously-signaled hEvents it will stop working.   
               
            Thus, it is imperative to call ResetEvent every time you finish a data transfer.   
            • 3. Re: unexpected WaitForXfer() behavior
              andrew.macdonald

              That's good advice. Thank you.   
                 
              ...but my problem appears to be that hEvent does not get signalled in the first place. Using a USB protocol-analyzer program, I can see the requests (from BeginDataXfer) going out to my endpoint and I can see requested the data arriving from the endpoint. In my app, however, the call to WaitForXfer always times-out, which seems to indicate that hEvent is never signalled, even though I know the data has already arrived.   
                 
              ...*time passes*...   
                 
              You just made me think of something and I tried it:   
                 
              Consider the following snippet from the (working) Screamer example code.   
                 
              if (!EndPoint.WaitForXfer(ovLapStatus->hEvent, 500))   
              {   
              EndPoint.Abort();   
              PInvoke.WaitForSingleObject(ovLapStatus->hEvent, 500);   
              }   
                 
              In my modified version, I had gotten rid of the WaitForSingleObject call because I figured it was redundant, so only the WaitForXfer call remained. Just now, I got rid of WaitForXfer completely and replaced it with a call to WaitForSingleObject, so that my code is now something like this:   
                 
              if (PInvoke.WaitForSingleObject(ovLapStatus->hEvent, 500) != 0) // signalled == 0   
              {   
              // do stuff   
              }   
                 
              ...and suddenly it works!   
                 
              WaitForSingleObject sees the signalled hEvent and WaitForXfer does not, even though it may be a wrapper for WaitForSingleObject. WaitForXfer isn't needed at all. It seems we may have found a bug in the SuiteUSB 2.0 version of CyUSB. I wonder how version 3.4 behaves...   
                 
              Note: FinishDataXfer appears to properly reset the event, so it seems a call to ResetEvent isn't needed.   
                 
              Thanks!   
              • 4. Re: unexpected WaitForXfer() behavior
                andrew.sobotka
                        Judging from your discovery, I agree that there seems to be something wrong with their WaitForXfer. I would try breaking on it and stepping through in the debugger. You might need to fiddle with Visual Studio to get it to show unmanaged code. You should be able to find the call to WFSO and you can check it's return value there, too.   
                   
                The call to WFSO after the call to Abort is designed to allow the device to be disconnected without locking up the computer. In the original, the call to WFSO should have had an infinite timeout, because you want to give the USB driver enough time to clean up before freeing user-mode buffers.   
                   
                Please post again with any news about whether this bug applies to 3.4 as well. And thanks for the note about FinishDataXfer, I did not know it clears the hEvent member. It would be nice if Cypress would mention this in their documentation...   
                • 5. Re: unexpected WaitForXfer() behavior
                  andrew.sobotka
                          I need to post a correction. GetOverlappedResult is how you read the number of bytes actually transferred, and whether the asynchronous I/O operation has completed. It is a *function* and not a *macro*.   
                     
                  The macro I was thinking about is HasOverlappedIoCompleted. It checks the hEvent member directly, without having to call into the OS, but it specifies no timeout.   
                  • 6. Re: unexpected WaitForXfer() behavior
                    andrew.sobotka
                            I discovered that the Event which is created by Cypress is by default an auto-reset event. This is why it is cleared automatically every time a thread wakes up on it. It is not FinishDataXfer that resets the event.