Re: Porting libusbK software to Linux/Mac

Isaac Abbott <[email protected]> Tue, 7 Jun 2016 12:09:10 +0000
Newsgroups gmane.comp.lib.libusb.devel.windows
Message-ID <141AAF51821A9347ADD1E35BF7F25191B3A8FCF8@Exchange.mtiinstruments.local>
Hi Chris,

I did something similar for a project last year. I use both UsbK and StmK to communicate to a device with two bulk endpoints. The device uses one endpoint for 2-way command-response channel and the second endpoint for a higher speed 1-way “streaming” data only channel.

I ported over to libusb-1.0 for a Linux application. The source is separated by #define USE_LIBUSBK.
There is no direct equivalent to StmK, so I had to do a lot more work to replicate the same functionality ( see StreamThread() ).
I provide my source as a reference only and make no guarantees as to its correctness or robustness.

Best Regards,
Isaac

From: Chris E [mailto:[email protected]]
Sent: Tuesday, June 07, 2016 2:28 AM
To: [email protected]
Subject: [Libusb-win32-devel] Porting libusbK software to Linux/Mac

Hi guys.
As the title suggests, I need to port some libusbK code to a different library in order to make it cross-platform.

The software uses both control requests handled by UsbK, and isochronous requests handled by StmK.

I've looked into libusb-1.0, and it looks like it will be simple to port the UsbK code.  However, I couldn't see any immediate equivalent to StmK.

Is there a way to set up something similar to StmK in libusb-1.0?  Alternatively, is there another library with cross-platform support and an StmK-like module?

Thanks,
~Chris

------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity 
planning reports. https://ad.doubleclick.net/ddm/clk/305295220;132659582;e

_______________________________________________
Libusb-win32-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libusb-win32-devel
USBCommunicationsInterface.cpp (text/plain, 54.1 KB)
/////////////////////////////////////////////////////////////////////////////
// USBCommunicationsInterface.cpp : implementation file
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "USBCommunicationsInterface.h"
#include "mti_lib/cpp/common/HighResolutionTimer.h"

////////////////////////////////////////////////////////////////////////////
// DECLARATIONS
////////////////////////////////////////////////////////////////////////////
#define COMMAND_EP_WRITE   0x01
#define COMMAND_EP_READ    0x81
#define STREAM_EP_READ     0x82

//----------------------------------------
// STREAMING
//
// It is unclear what the ideal values for
// these parameters are. These values were
// copied directly from an example because
// they work.
//----------------------------------------
#define MAX_XFER_SIZE     4096    // Maximum number of bytes transferred at once (see UsbK_GetPipePolicy)
#define MAX_PEND_XFERS    128     // Maximum number of transfers to store at one time
#define MAX_PEND_IO       3       // Maximum number of overlapped IO transfers pending at one time


CUSBCommunicationsInterface::CUSBCommunicationsInterface(void)
//=====================================================================
//=====================================================================
{
  mpDeviceNames   = 0;
  mpDeviceModel   = 0;
  mpDeviceSerial  = 0;
  mpDeviceVersion = 0;
  mDeviceCount    = 0;
  mDeviceIndex    = -1;

#ifdef USE_LIBUSBK
  mpKDeviceList   = 0;
  mKDeviceList    = NULL;
  mKHandle        = NULL;
  mKStmHandle     = NULL;
#else
  mHandle         = NULL;
  mpStreamThread  = 0;
  mStreamState    = 0;

  mPartialDataIndex = 0;
  mPartialDataItem  = NULL;
  mPendingIO		    = 0;
  mPendingTransfer  = 0;

  libusb_init(NULL);
  libusb_set_debug(NULL, 3);
  
#if defined LINUX_TARGET
  sem_init(&mStopStream, 0, 0);
  sem_init(&mStreamStartStop, 0, 0);
  pthread_mutex_init(&mCS, NULL);
#endif

#endif

  mVID            = MTI_VID;         //MTII
  mPID            = DACC_PID;        //default to DACC

  mPrintTraceMessages = TRUE;
}

CUSBCommunicationsInterface::~CUSBCommunicationsInterface(void)
//=====================================================================
//=====================================================================
{
  if(mpDeviceNames)
  {
    delete[] mpDeviceNames;
    mpDeviceNames = 0;
  }

  if(mpDeviceModel)
  {
    delete[] mpDeviceModel;
    mpDeviceModel = 0;
  }

  if(mpDeviceSerial)
  {
    delete[] mpDeviceSerial;
    mpDeviceSerial = 0;
  }

  if(mpDeviceVersion)
  {
    delete[] mpDeviceVersion;
    mpDeviceVersion = 0;
  }

  mDeviceCount = 0;
  mDeviceIndex = -1;

#ifdef USE_LIBUSBK
  if(mKStmHandle)
  {
    StmK_Free(mKStmHandle);
    mKStmHandle = 0;
  }

  if(mKHandle)
  {
    mKUsb.Free(mKHandle);
    mKHandle = NULL;
  }

  if(mKDeviceList)
  {
    LstK_Free(mKDeviceList);
    mKDeviceList = NULL;
  }

  if(mpKDeviceList)
  {
    delete[] mpKDeviceList;
    mpKDeviceList = 0;
  }

#else
  if(mHandle)
  {
    libusb_close(mHandle);
    mHandle = NULL;
  }
  
  libusb_exit(NULL);

#if defined LINUX_TARGET
  pthread_mutex_destroy(&mCS);
#endif

#endif
}

int CUSBCommunicationsInterface::GetDeviceIndex( CString name )
//=====================================================================
// GetDeviceIndex - Scan our list of USB Devices 
//                  for one with name.
//
//             Return -1 if not found
//             Index number if found
//=====================================================================
{
  for(int i=0; i<mDeviceCount; i++)
  {
    if( mpDeviceNames[i].Compare(name) == 0 )
    {
      return i;
    }
  }

  return -1;
}

CString CUSBCommunicationsInterface::GetDeviceName( int index )
//=====================================================================
// GetDeviceName - Return the name string at index.
//
//             Return NULL if bad index
//=====================================================================
{
  if(index < mDeviceCount)
  {
    return mpDeviceNames[index];
  }
  else
  {
    return CString("");
  }
}

CString CUSBCommunicationsInterface::GetDeviceModelNumber( int index )
//=====================================================================
// GetDeviceModelNumber - Return the model number string at index.
//
//             Return NULL if bad index
//=====================================================================
{
  if(index < mDeviceCount)
  {
    return mpDeviceModel[index];
  }
  else
  {
    return CString("");
  }
}

CString CUSBCommunicationsInterface::GetDeviceSerialNumber( int index )
//=====================================================================
// GetDeviceSerialNumber - Return the serial number string at index.
//
//             Return NULL if bad index
//=====================================================================
{
  if(index < mDeviceCount)
  {
    return mpDeviceSerial[index];
  }
  else
  {
    return CString("");
  }
}

CString CUSBCommunicationsInterface::GetDeviceSoftwareVersion( int index )
//=====================================================================
// GetDeviceSoftwareVersion - Return the software version string at index.
//
//             Return NULL if bad index
//=====================================================================
{
  if(index < mDeviceCount)
  {
    return mpDeviceVersion[index];
  }
  else
  {
    return CString("");
  }
}

int CUSBCommunicationsInterface::ConnectToDevice( CString name )
//=====================================================================
// ConnectToDevice - Attempt to open a connection to device by name
//
//                   Return 0 if success
//=====================================================================
{
  int index = GetDeviceIndex(name);

  if(index >= 0)
  {
    return ConnectToDevice(index);
  }
  else
  {
    return 1;
  }
}

void CUSBCommunicationsInterface::Disconnect(void)
//=====================================================================
// Disconnect - Disconnect from all USB devices & free handle
//=====================================================================
{
  StopStream();

  //------------------------------------------------------------------
  // Release the interface
  //------------------------------------------------------------------
#ifdef USE_LIBUSBK
  if(mKHandle)
  {
    mKUsb.ReleaseInterface(mKHandle,0,TRUE);
    mKUsb.Free(mKHandle);
    mKHandle = NULL;
  }
#else
  if(mHandle)
  {
    libusb_release_interface(mHandle, 0);
    libusb_close(mHandle);
    mHandle = NULL;
  }

#endif

  mDeviceIndex = -1;
}

bool CUSBCommunicationsInterface::IsConnected(void)
//=====================================================================
//=====================================================================
{
#ifdef USE_LIBUSBK
  if(mKHandle != NULL && mDeviceIndex != -1)
#else
  if(mHandle != NULL && mDeviceIndex != -1)
#endif
  {
    return true;
  }
  else
  {
    return false;
  }
}

bool CUSBCommunicationsInterface::ReadyToStream(void)
//=====================================================================
//=====================================================================
{
#ifdef USE_LIBUSBK
  if(mKStmHandle != NULL)
#else
  if( mStreamState == 1 )
#endif
  {
    return true;
  }
  else
  {
    return false;
  }
}

int CUSBCommunicationsInterface::WriteCommand(const char *buffer, int bytesToWrite, int *bytesWritten)
//=====================================================================
// Write - Write data to USB port
//
// INPUTS:   buffer       - buffer contining data to write
//           bytesToWrite - number of bytes to write
//
// OUTPUTS:  bytesWritten - bytes actually written
//
// RETURNS:  status code
//            0 = OK
//           -1 = device not open
//            8 = Error From WritePipe
//            10 = Write Timeout
//=====================================================================
{
  if(bytesWritten)
    *bytesWritten = 0;

#ifdef USE_LIBUSBK
  if(!mKHandle)
#else
  if(!mHandle)
#endif
  {
    return -1;
  }

  if( bytesToWrite <= 0 )
  {
    return 0;
  }

  //------------------------------------------------------------------
  // Set the Timeout of WritePipe() for the Command OUT endpoint
  //------------------------------------------------------------------
#ifdef USE_LIBUSBK
  UINT newPipeTimeout = 1000;
  BOOL success = mKUsb.SetPipePolicy(mKHandle, COMMAND_EP_WRITE, PIPE_TRANSFER_TIMEOUT, sizeof(UINT), &newPipeTimeout);
  if (!success)
  {
    if(mPrintTraceMessages)
    {
      TRACE("Error From SetPipePolicy(USB)=%d\n", GetLastError());
    }
    return 9;
  }
#endif

#ifdef USE_LIBUSBK
  PUCHAR sendBuffer = (PUCHAR)buffer;
  UINT bufferLength = bytesToWrite;
  UINT lengthTransferred = 0;
  success = mKUsb.WritePipe(mKHandle, COMMAND_EP_WRITE, sendBuffer, bufferLength, &lengthTransferred, NULL);

  if(success)
  {
    *bytesWritten = lengthTransferred;
    return 0;
  }
  else
  {
    return 8;
  }
#else
  unsigned char * sendBuffer = (unsigned char *)buffer;
  int bufferLength = bytesToWrite;
  int lengthTransferred = 0;
  int rc = libusb_bulk_transfer(mHandle, COMMAND_EP_WRITE, sendBuffer, bufferLength,  &lengthTransferred, 1000);

  if(rc == 0)
  {
    *bytesWritten = lengthTransferred;
    return 0;
  }
  else if(rc == LIBUSB_ERROR_TIMEOUT)
  {
    return 10;
  }
  else
  {
    return 8;
  }
#endif
}

int CUSBCommunicationsInterface::ReadResponse(char *buffer, int maxBytesToRead, int *bytesRead, int tmo)
//=====================================================================
// Read - Read a line from USB command IN endpoint
//
// INPUTS:   buffer         - read buffer
//           maxBytesToRead - max size of read buffer
//           timeout        - timeout (milliseconds)
//
// OUTPUTS:  bytesRead - bytes actually read
//
// RETURNS:  status code
//            0 = OK
//           -1 = Port not open
//            9 = Error From ReadPipe
//           10 = Read Timeout
//=====================================================================
{
  CString text;

  if(bytesRead)
    *bytesRead = 0;

  if(buffer) 
    memset(buffer, 0, maxBytesToRead);

#ifdef USE_LIBUSBK
  if(!mKHandle)
#else
  if(!mHandle)
#endif
  {
    return -1;
  }

  if( maxBytesToRead <= 0 )
  {
    return 0;
  }

#ifdef USE_LIBUSBK
  //------------------------------------------------------------------
  // Set the Timeout of ReadPipe() for the Command IN endpoint
  //------------------------------------------------------------------
  UINT newPipeTimeout = tmo;
  BOOL success = mKUsb.SetPipePolicy(mKHandle, COMMAND_EP_READ, PIPE_TRANSFER_TIMEOUT, sizeof(UINT), &newPipeTimeout);
  if (!success)
  {
    if(mPrintTraceMessages)
    {
      TRACE("Usb.SetPipePolicy failed. (0x%08X)\n", GetLastError());
    }
    return 9;
  }
#endif

  CHighResolutionTimer timer;
  timer.StartTimer();

  unsigned char internalBuffer[64];
  int count = 0;

#ifdef USE_LIBUSBK
  UINT numRead = 0;
  while(1)
  {
    success = mKUsb.ReadPipe(mKHandle, COMMAND_EP_READ, &internalBuffer[0], 1, &numRead, NULL);

    if( !success )
    {
      if(mPrintTraceMessages)
      {
        text.Format("Error From ReadPipe(USB)=%d\n", GetLastError());
        TRACE((const char*)text);
      }

      *bytesRead = count;
      return 9;
    }
    else if(numRead == 0)
    {
      if(mPrintTraceMessages)
      {
        text.Format("Timeout From ReadPipe(USB)=%d\n", GetLastError());
        TRACE((const char*)text);
      }

      *bytesRead = count;
      return 10;
    }
    else if(internalBuffer[0] < ' ')
    {
      if(internalBuffer[0] == '\n')
      {
        *bytesRead = count;
        break;
      }
    }
    else if(*bytesRead < maxBytesToRead)
    {
      buffer[count] = internalBuffer[0];
      count += numRead;
    }

    // Check for timeout
    if( tmo > 0 )
    {
      if( timer.GetTimeInMilliSeconds() > (double)tmo )
      {
        *bytesRead = count;
        return 10;
      }
    }
  }
#else
  int numRead = 0;
  while(1)
  {
    int rc = libusb_bulk_transfer(mHandle, COMMAND_EP_READ, &internalBuffer[0], 64, &numRead, (unsigned int)tmo);

    if( rc == LIBUSB_ERROR_TIMEOUT)
    {
      if(mPrintTraceMessages)
      {
        text.Format("Timeout From ReadPipe(USB)=%d\n", rc);
        TRACE((const char*)text);
      }

      *bytesRead = count;
      return 10;
    }
    else if( rc != 0 )
    {
      if(mPrintTraceMessages)
      {
        text.Format("Error From ReadPipe(USB)=%d\n", rc);
        TRACE((const char*)text);
      }

      *bytesRead = count;
      return 9;
    }
    else
    {
      if(numRead == 0)
      {
        if(mPrintTraceMessages)
        {
          text.Format("No Data From ReadPipe(USB)=%d\n", rc);
          TRACE((const char*)text);
        }

        *bytesRead = count;
        return 9;
      }
      else if(internalBuffer[numRead-1] < ' ')
      {
        if(internalBuffer[numRead-1] == '\n')
        {
          memcpy(&buffer[count], &internalBuffer[0], numRead-1);
          count += (numRead-1);
          *bytesRead = count;
          break;
        }
      }
      else if(*bytesRead < maxBytesToRead)
      {
    	  memcpy(&buffer[count], &internalBuffer[0], numRead);
        count += numRead;
      }
    }

    // Check for timeout
    if( tmo > 0 )
    {
      if( timer.GetTimeInMilliSeconds() > (double)tmo )
      {
        *bytesRead = count;
        return 10;
      }
    }

  }
#endif

  return 0;
}

int CUSBCommunicationsInterface::ReadStreaming(char *buffer, int minBytesToRead, int maxBytesToRead, int *bytesRead, int tmo)
//=====================================================================
// ReadStreaming - Read data from USB Streaming IN endpoint.
//
// INPUTS:   buffer         - read buffer
//           minBytesToRead - min number of bytes to read before returning (set to -1 if unused)
//           maxBytesToRead - max size of read buffer
//           timeout        - timeout (milliseconds)
//
// OUTPUTS:  bytesRead - bytes actually read
//
// RETURNS:  status code
//            0 = OK
//           -1 = Port not open
//            9 = Error From ReadPipe
//           10 = Read Timeout
//=====================================================================
{
  if(bytesRead)
    *bytesRead = 0;

#ifdef USE_LIBUSBK
  if(!mKStmHandle)
#else
    if(!mpStreamThread)
#endif
  {
    return -1;
  }

  if( maxBytesToRead <= 0 || minBytesToRead <= 0 )
  {
    return 0;
  }

  if(buffer) 
    memset(buffer, 0, maxBytesToRead);

  CHighResolutionTimer timer;
  timer.StartTimer();

  int retVal = 0;

  unsigned char * recBuffer = (unsigned char *)buffer;
  UINT bufferLength = maxBytesToRead;
  int totalTransferred = 0;

  while( totalTransferred < minBytesToRead )
  {
    UINT transferred = 0;
#ifdef USE_LIBUSBK
    BOOL success = StmK_Read(mKStmHandle, recBuffer, 0, bufferLength, &transferred);
    if(success)
    {
      totalTransferred += transferred;
      bufferLength -= transferred;
      recBuffer = (PUCHAR)&buffer[totalTransferred];
    }
    else
    {
      // If the return result is ERROR_NO_MORE_ITEMS then there is no more data
      // to read.  Other errors indicate a problem.
      if (GetLastError() != ERROR_NO_MORE_ITEMS)
      {
        retVal = 9;
        break;
      }
      // Handle ERROR_NO_MORE_ITEMS:
      // Do nothing
    }
#else
    int rc = StreamRead(recBuffer, bufferLength, &transferred);
    if(rc == 0)
    {
      totalTransferred += transferred;
      bufferLength -= transferred;
      recBuffer = (unsigned char *)&buffer[totalTransferred];
    }
    else
    {
      // If the return result is ERROR_NO_MORE_ITEMS then there is no more data
      // to read.  Other errors indicate a problem.
      if (rc != 1)
      {
        retVal = 9;
        break;
      }
      // Handle ERROR_NO_MORE_ITEMS:
      // Do nothing
    }
#endif

    // Check for timeout
    if( tmo > 0 )
    {
      if( timer.GetTimeInMilliSeconds() > (double)tmo )
      {
        retVal = 10;
        break;
      }
    }
  }

  *bytesRead = totalTransferred;
  return retVal;
}

int CUSBCommunicationsInterface::FlushCommand(void)
//=====================================================================
// FlushCommand - Flush command endpoint input and output pipes
//
// INPUTS:   none
//
// OUTPUTS:  none
//
// RETURNS:  status code
//           -1 = Port not open
//            0 = OK
//            7 = Error From FlushPipe
//=====================================================================
{ 
#ifdef USE_LIBUSBK
  if(!mKHandle)
  {
    return -1;
  }

  if( mKUsb.FlushPipe(mKHandle, COMMAND_EP_READ) == false )
  {
    return 7;
  }

  if( mKUsb.FlushPipe(mKHandle, COMMAND_EP_WRITE) == false )
  {
    return 7;
  }
#endif

  return 0;
}

int CUSBCommunicationsInterface::FlushStreaming(void)
//=====================================================================
// FlushStreaming - Flush streaming endpoint input pipe
//
// Note: This is not normally called
//
// INPUTS:   none
//
// OUTPUTS:  none
//
// RETURNS:  status code
//           -1 = Port not open
//            0 = OK
//            7 = Error From FlushPipe
//=====================================================================
{
#ifdef USE_LIBUSBK
  if(!mKHandle)
  {
    return -1;
  }

  if( mKUsb.FlushPipe(mKHandle, STREAM_EP_READ) == false )
  {
    return 7;
  }
#endif

  return 0;
}

//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
// LIBUSBK
#ifdef USE_LIBUSBK

//int CUSBCommunicationsInterface::FindAllDevices(int maxDevices)
//{

//}

int CUSBCommunicationsInterface::FindAllDevices(int maxDevices)
//=====================================================================
// FindAllUSBDevices - Scan USB Devices for ones that match
//                     MTII USB VID and PID.
//
//             Allocate and build list of USB Devices
//             Return number of devices found
//=====================================================================
{     
  BYTE buffer1[128];
  BYTE buffer2[128];

  //---------------------------
  // Delete previous nodes list
  //---------------------------
  if(mpKDeviceList)
  {
    delete[] mpKDeviceList;
    mpKDeviceList = 0;
  }

  if(mpDeviceNames)
  {
    delete[] mpDeviceNames;
    mpDeviceNames = 0;
  }

  if(mpDeviceModel)
  {
    delete[] mpDeviceModel;
    mpDeviceModel = 0;
  }

  if(mpDeviceSerial)
  {
    delete[] mpDeviceSerial;
    mpDeviceSerial = 0;
  }

  if(mpDeviceVersion)
  {
    delete[] mpDeviceVersion;
    mpDeviceVersion = 0;
  }

  mDeviceCount  = 0;
  
  //-----------------------
  // Allocate new device list
  //-----------------------
  if(maxDevices > 0)
  {
    mpKDeviceList   = new KLST_DEVINFO_HANDLE[maxDevices];
    mpDeviceNames   = new CString[maxDevices];
    mpDeviceModel   = new CString[maxDevices];
    mpDeviceSerial  = new CString[maxDevices];
    mpDeviceVersion = new CString[maxDevices];
  }

  //------------------------------------------------------------------
  // Get the device list & make sure one or more devices were found
  //------------------------------------------------------------------
  UINT deviceCount = 0;
  KLST_DEVINFO_HANDLE deviceInfo = NULL;

  if ( !LstK_Init(&mKDeviceList, (KLST_FLAG)0) )
  {
    if(mPrintTraceMessages)
    {
      TRACE("Error Initializing USB Device List.\n");
    }
    return 0;
  }

  LstK_Count(mKDeviceList, &deviceCount);

  if(deviceCount == 0)
  {
    if(mPrintTraceMessages)
    {
      TRACE("Device list empty.\n");
    }
    SetLastError(ERROR_DEVICE_NOT_CONNECTED);

    // If LstK_Init returns TRUE, the list must be freed.
    LstK_Free(mKDeviceList);

    return 0;
  }

  //------------------------------------------------------------------
  // Look For MTII USB devices
  //------------------------------------------------------------------
  if(mPrintTraceMessages)
  {
    TRACE("Looking for USB Devices by vid/pid %04X/%04X..\n", mVID, mPID);
  }

  //------------------------------------------------------------------
  // Enumerate the device list using it's internal "current" position.
  //------------------------------------------------------------------
  unsigned int lengthTransferred;

  LstK_MoveReset(mKDeviceList);      //Reset the device list position
  
  //------------------------------------------------------------------
  // Call LstK_MoveNext after a LstK_MoveReset to advance to the first element.
  //------------------------------------------------------------------
  while( LstK_MoveNext(mKDeviceList, &deviceInfo) && ( mDeviceCount < maxDevices) )
  {
    if( deviceInfo->Common.Vid == mVID && deviceInfo->Common.Pid == mPID )
    {
      //------------------------------------------------------------------
      // Load the device driver
      //------------------------------------------------------------------
      LibK_LoadDriverAPI(&mKUsb, deviceInfo->DriverID);
    
      //------------------------------------------------------------------
      // Initialize the device
      //------------------------------------------------------------------
      mKUsb.Init(&mKHandle, deviceInfo);

      //------------------------------------------------------------------
      // Get the Friendly Name of the device 
      //
      // 0x0409 is the USB-IF language index for American English
      //------------------------------------------------------------------
      memset(buffer1, 0, sizeof(buffer1));

      BOOL fname_retv = mKUsb.GetDescriptor(mKHandle, USB_DESCRIPTOR_TYPE_STRING, 6, 0x0409, buffer1, sizeof(buffer1), &lengthTransferred);

      PUSB_STRING_DESCRIPTOR name = (PUSB_STRING_DESCRIPTOR)buffer1;

      //------------------------------------------------------------------
      // Get the model number of the device 
      //
      // 0x0409 is the USB-IF language index for American English
      //------------------------------------------------------------------
      memset(buffer2, 0, sizeof(buffer2));

      BOOL model_retv = mKUsb.GetDescriptor(mKHandle, USB_DESCRIPTOR_TYPE_STRING, 7, 0x0409, buffer2, sizeof(buffer2), &lengthTransferred);

      PUSB_STRING_DESCRIPTOR model = (PUSB_STRING_DESCRIPTOR)buffer2;

      //------------------------------------------------------------------
      // Save Serial Number
      //------------------------------------------------------------------
      mpDeviceSerial[mDeviceCount] = deviceInfo->SerialNumber;

      //------------------------------------------------------------------
      // Free the device handle
      //------------------------------------------------------------------
      mKUsb.Free(mKHandle);

      //------------------------------------------------------------------
      // Save the friendly name of the device & deviceInfo pointer
      //------------------------------------------------------------------
      mpKDeviceList[mDeviceCount]    = deviceInfo;
      if(fname_retv)
        mpDeviceNames[mDeviceCount] = name->bString;
      else
        mpDeviceNames[mDeviceCount] = "unavailable";

      if(model_retv)
        mpDeviceModel[mDeviceCount] = model->bString;
      else
        mpDeviceModel[mDeviceCount] = "unavailable";

      mpDeviceVersion[mDeviceCount] = "";
      ++mDeviceCount;

      //------------------------------------------------------------------
      // print some information about the device.
      //------------------------------------------------------------------
      if(mPrintTraceMessages)
      {
        TRACE("[%d]: %ls %04X:%04X (%s-%s): %s - %s\n",
            mDeviceCount,
            name->bString,
            deviceInfo->Common.Vid,
            deviceInfo->Common.Pid,
            deviceInfo->Common.InstanceID,
            deviceInfo->SerialNumber,
            deviceInfo->DeviceDesc,
            deviceInfo->Mfg);
      }
    }  
  }

  return mDeviceCount;
}

int CUSBCommunicationsInterface::ConnectToDevice( int index )
//=====================================================================
// ConnectToDevice - Attempt to open a connection to device by index
//
//                   Return 0 if success
//                          1 if set-up failure
//                          2 if device busy
//=====================================================================
{
  if(index >= mDeviceCount)
  {
    return 1;
  }

  //------------------------------------------------------------------
  // Load the device driver
  //------------------------------------------------------------------
  if( !LibK_LoadDriverAPI(&mKUsb, mpKDeviceList[index]->DriverID) )
  {
    if(mPrintTraceMessages)
    {
      TRACE("LibK_LoadDriverAPI failed. ErrorCode: %08Xh\n", GetLastError());
    }
    return 1;
  }

  //------------------------------------------------------------------
  // Initialize the device
  //------------------------------------------------------------------
  if (!mKUsb.Init(&mKHandle, mpKDeviceList[index]))
  {
    if(mPrintTraceMessages)
    {
      TRACE("Usb.Init failed. ErrorCode: %08Xh\n", GetLastError());
    }
    return 1;
  }

  mDeviceIndex = index;

  //------------------------------------------------------------------
  // Claim the interface
  //  - This step is not necessary when using WinUSB, but is included
  //    for completeness
  //------------------------------------------------------------------
  BOOL success = mKUsb.ClaimInterface(mKHandle,0,TRUE);
  if( !success )
  {
    DWORD err = GetLastError();
    if( err == ERROR_BUSY )
    {
      TRACE("Usb.ClaimInterface failed - Device Busy");
      return 2;
    }
    else
    {
      TRACE("Usb.ClaimInterface failed. (0x%08X)\n", err);
      return 1;
    }
  }

  //------------------------------------------------------------------
  // Set the Timeout of ReadPipe() for the Command IN endpoint
  //------------------------------------------------------------------
  UINT newPipeTimeout = 10000; //Time-out in milliseconds
  success = mKUsb.SetPipePolicy(mKHandle, COMMAND_EP_READ, PIPE_TRANSFER_TIMEOUT, sizeof(UINT), &newPipeTimeout);
  if (!success && mPrintTraceMessages)
  {
    TRACE("Usb.SetPipePolicy failed. (0x%08X)\n", GetLastError());
  }

  //------------------------------------------------------------------
  // Start streaming thread on Streaming IN endpoint
  //------------------------------------------------------------------
  int rc = StartStream();
  if( rc != 0 && mPrintTraceMessages)
  {
    TRACE("StartStream() failed. ErrorCode: %08Xh\n", rc);
  }

  if(mPrintTraceMessages)
  {
    TRACE("Device opened successfully!\n");
  }

  return 0;
}

int CUSBCommunicationsInterface::StartStream(void)
//=====================================================================
// ConnectToStream - Initialize the Stream
//
// INPUTS:   none
//
// RETURNS:  status code
//            0 = OK
//           -1 = device not open
//            1 = error from Init
//            2 = error from Start
//=====================================================================
{
  if(mKStmHandle)
  {
    return 0;
  }

  if(!mKHandle)
  {
    return -1;
  }

  CString text;    
  BOOL success;

  //------------------------------------------------------------------
  // Initialize the stream.
  //------------------------------------------------------------------
  success = StmK_Init(&mKStmHandle, mKHandle, STREAM_EP_READ,
                      MAX_XFER_SIZE, MAX_PEND_XFERS, MAX_PEND_IO,
                      NULL, KSTM_FLAG_NONE);
  if (!success)
  {
    if(mPrintTraceMessages)
    {
      text.Format("StmK_Init failed. ErrorCode: %08Xh\n", GetLastError());
      TRACE((const char*)text);
    }
    return 1;
  }

  //------------------------------------------------------------------
  // Start the stream.
  //------------------------------------------------------------------
  success = StmK_Start(mKStmHandle);
  if (!success)
  {
    StmK_Free(mKStmHandle);
    mKStmHandle = 0;

    if(mPrintTraceMessages)
    {
      text.Format("StmK_Start failed. ErrorCode: %08Xh\n", GetLastError());
      TRACE((const char*)text);
    }
    return 2;
  }

  return 0;
}

void CUSBCommunicationsInterface::StopStream(void)
//=====================================================================
// StopStream - Stop the stream and free resources.
//
// According to the documentation StmK_Free will also stop the stream
// before freeing resources, but I found that if the device has been
// disconnected StmK_Stop will fail and StmK_Free will crash the program.
//=====================================================================
{
  BOOL success;
  CString text;

  if(mKStmHandle)
  {
    success = StmK_Stop(mKStmHandle, 0);
    if (!success)
    {
      if(mPrintTraceMessages)
      {
        text.Format("StmK_Stop failed. ErrorCode: %08Xh\n", GetLastError());
        TRACE((const char*)text);
      }
    }
    else
    {
      StmK_Free(mKStmHandle);
    }
    mKStmHandle = 0;
  }
}


int CUSBCommunicationsInterface::ReadCommand(char *buffer, int maxBytesToRead, int *bytesRead, int tmo)
//=====================================================================
// Read - Read data from USB command IN endpoint
//
// INPUTS:   buffer         - read buffer
//           maxBytesToRead - max size of read buffer
//           timeout        - timeout (milliseconds)
//
// OUTPUTS:  bytesRead - bytes actually read
//
// RETURNS:  status code
//            0 = OK
//           -1 = Port not open
//            9 = Error From ReadPipe
//           10 = Read Timeout
//
//
//     ---- THIS FUNCTION IS UNUSED ----
//=====================================================================
{
  if(bytesRead)
    *bytesRead = 0;

  if(buffer) 
    memset(buffer, 0, maxBytesToRead);

  if(!mKHandle)
  {
    return -1;
  }

  if( maxBytesToRead <= 0 )
  {
    return 0;
  }

  PUCHAR recBuffer = (PUCHAR)buffer;
  UINT bufferLength = maxBytesToRead;
  UINT lengthTransferred;
  BOOL success = mKUsb.ReadPipe(mKHandle, COMMAND_EP_READ, recBuffer, bufferLength, &lengthTransferred, NULL);

  if(success)
  {
    *bytesRead = lengthTransferred;
    return 0;
  }
  else
  {
    return 9;
  }
}

//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
//**********************************************************************************
// LibUsb 1.0 
#else


int CUSBCommunicationsInterface::FindAllDevices(int maxDevices)
//=====================================================================
// FindAllUSBDevices - Scan USB Devices for ones that match
//                     MTII USB VID and PID.
//
//             Allocate and build list of USB Devices
//             Return number of devices found
//=====================================================================
{     
  const char * buffer1[128];

  //---------------------------
  // Delete previous nodes list
  //---------------------------
  if(mpDeviceNames)
  {
    delete[] mpDeviceNames;
    mpDeviceNames = 0;
  }

  if(mpDeviceModel)
  {
    delete[] mpDeviceModel;
    mpDeviceModel = 0;
  }

  if(mpDeviceSerial)
  {
    delete[] mpDeviceSerial;
    mpDeviceSerial = 0;
  }

  if(mpDeviceVersion)
  {
    delete[] mpDeviceVersion;
    mpDeviceVersion = 0;
  }

  mDeviceCount  = 0;
  
  //-----------------------
  // Allocate new device list
  //-----------------------
  if(maxDevices > 0)
  {
    mpDeviceNames   = new CString[maxDevices];
    mpDeviceModel   = new CString[maxDevices];
    mpDeviceSerial  = new CString[maxDevices];
    mpDeviceVersion = new CString[maxDevices];
  }

  //------------------------------------------------------------------
  // Get the device list & make sure one or more devices were found
  //------------------------------------------------------------------
  libusb_device **devs;

	ssize_t cnt = libusb_get_device_list(NULL, &devs);
	if (cnt < 0)
  {
    if(mPrintTraceMessages)
    {
      TRACE("Error Initializing USB Device List.\n");
    }
    return 0;
  }
  else if(cnt == 0)
  {
    if(mPrintTraceMessages)
    {
      TRACE("Device list empty.\n");
    }
    libusb_free_device_list(devs, 1);
    return 0;
  }

  //------------------------------------------------------------------
  // Look For MTII USB devices
  //------------------------------------------------------------------
  if(mPrintTraceMessages)
  {
    TRACE("Looking for USB Devices by vid/pid %04X/%04X..\n", mVID, mPID);
  }

  libusb_device *dev;
	int i = 0;

	while ((dev = devs[i++]) != NULL) 
  {
		struct libusb_device_descriptor desc;
		int r = libusb_get_device_descriptor(dev, &desc);
		if (r == 0) 
    {
      if( desc.idVendor == mVID && desc.idProduct == mPID )
      {
        libusb_device_handle *handle;
        
        r = libusb_open(dev, &handle);
        if (r == 0)
        {
          //*******************************
          // String descriptors
          //*******************************
          //  0  g_pLangDescriptor,
          //  1  g_pManufacturerString,
          //  2  g_pProductString,
          //  3  g_pSerialNumberString,
          //  4  g_pConfigString,
          //  5  g_pInterfaceString,
          //  6  g_pFriendlyNameString,
          //  7  g_pModelNumberString,
          //  8  g_pFirmwareVersionString

          r = libusb_get_string_descriptor_ascii(handle, 3, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
            mpDeviceSerial[mDeviceCount] = CString((const char*)buffer1);
          }
          else
          {
            mpDeviceSerial[mDeviceCount] = "unavailable";
          }

          r = libusb_get_string_descriptor_ascii(handle, 6, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
			mpDeviceNames[mDeviceCount] = CString((const char*)buffer1);
          }
          else
          {
            mpDeviceNames[mDeviceCount] = "unavailable";
          }

          r = libusb_get_string_descriptor_ascii(handle, 7, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
			mpDeviceModel[mDeviceCount] = CString((const char*)buffer1);
          }
          else
          {
            mpDeviceModel[mDeviceCount] = "unavailable";
          }

          r = libusb_get_string_descriptor_ascii(handle, 8, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
			mpDeviceVersion[mDeviceCount] = CString((const char*)buffer1);
          }
          else
          {
            mpDeviceVersion[mDeviceCount] = "unavailable";
          }

          libusb_close(handle);

          ++mDeviceCount;

        }
      }
    }
	}

	libusb_free_device_list(devs, 1);

  return mDeviceCount;
}

int CUSBCommunicationsInterface::ConnectToDevice( int index )
//=====================================================================
// ConnectToDevice - Attempt to open a connection to device by index
//
//                   Return 0 if success
//                          1 if set-up failure
//                          2 if device busy
//=====================================================================
{
  char buffer1[128];

  if(index >= mDeviceCount)
  {
    return 1;
  }

  //***************************************************************
  // Search attached USB devices to find one that matches the index
  //***************************************************************
  libusb_device **devs;

	ssize_t cnt = libusb_get_device_list(NULL, &devs);
	if (cnt < 0)
  {
    return 1;
  }
  else if(cnt == 0)
  {
    libusb_free_device_list(devs, 1);
    return 1;
  }

  libusb_device *dev;
	int i = 0;
  int r;

	while ((dev = devs[i++]) != NULL) 
  {
		struct libusb_device_descriptor desc;
		r = libusb_get_device_descriptor(dev, &desc);
		if (r == 0) 
    {
      if( desc.idVendor == mVID && desc.idProduct == mPID )
      {
        libusb_device_handle *handle;
        
        r = libusb_open(dev, &handle);
        if (r == 0)
        {
          int matches = 0;

          r = libusb_get_string_descriptor_ascii(handle, 3, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
            if( strcmp(mpDeviceSerial[index], buffer1) == 0 )
            {
              matches++;
            }
          }

          r = libusb_get_string_descriptor_ascii(handle, 6, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
			      if( strcmp(mpDeviceNames[index], buffer1) == 0 )
            {
              matches++;
            }
          }

          r = libusb_get_string_descriptor_ascii(handle, 7, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
			      if( strcmp(mpDeviceModel[index], buffer1) == 0 )
            {
              matches++;
            }
          }

          r = libusb_get_string_descriptor_ascii(handle, 8, (unsigned char*)buffer1, 128);
          if (r >= 0) 
          {
			      if( strcmp(mpDeviceVersion[index], buffer1) == 0 )
            {
              matches++;
            }
          }

          if(matches > 0)     //If anything matches, we will assume it is the correct unit
          {
            mDeviceIndex = index;
            mHandle = handle;
          }
          else
          {
            libusb_close(handle);
          }
        }
      }
    }
	}

	libusb_free_device_list(devs, 1);

  if(mHandle == NULL)
  {
    return 1;
  }
        
  //------------------------------------------------------------------
  // Claim the interface
  //------------------------------------------------------------------
  r = libusb_claim_interface(mHandle, 0);
  if( r == LIBUSB_ERROR_BUSY )
  {
    if(mPrintTraceMessages)
    {
      TRACE("Usb.ClaimInterface failed - Device Busy");
    }
    return 2;
  }
  else if( r != 0 )
  {
    if(mPrintTraceMessages)
    {
      TRACE("Usb.ClaimInterface failed. (0x%08X)\n", r);
    }
    return 1;
  }

  //------------------------------------------------------------------
  // Start streaming thread on Streaming IN endpoint
  //------------------------------------------------------------------
  int rc = StartStream();
  if( rc != 0 && mPrintTraceMessages)
  {
    TRACE("StartStream() failed. ErrorCode: %08Xh\n", rc);
  }

  if(mPrintTraceMessages)
  {
    TRACE("Device opened successfully!\n");
  }

  return 0;
}

#if defined(_WINDOWS)
static UINT StartStreamThread(void *pObj)
//=====================================================================
// StartStreamThread -- Helper function for stream thread
//=====================================================================
{
  if(pObj)
  {
    return ((CUSBCommunicationsInterface *)pObj)->StreamThread();
  }

  return 0;
}

#elif defined LINUX_TARGET
void * StartStreamThread(void *pObj)
//=====================================================================
// StartStreamThread -- Helper function for starting a task
//=====================================================================
{
  if(pObj)
  {
    ((CUSBCommunicationsInterface *)pObj)->StreamThread();
  }

  return NULL;
}

#endif

int CUSBCommunicationsInterface::StartStream(void)
//=====================================================================
// ConnectToStream - Initialize the Stream
//
// INPUTS:   none
//
// RETURNS:  status code
//            0 = OK
//           -1 = device not open
//            1 = error from Init
//            2 = error from Start
//=====================================================================
{
  if(!mHandle)
  {
    return -1;
  }

  //----------------------------
  // Is streaming already Active
  //----------------------------
  if(mpStreamThread != 0)
  {
    return -1;
  }

  //------------------------------------------------------------------
  // Initialize the stream.
  //------------------------------------------------------------------
  mStreamState = 0;

  //-----------------
  // Reset our events
  //-----------------
#if defined(_WINDOWS)
  mStopStream.ResetEvent();
  mStreamStartStop.ResetEvent();
#elif defined(LINUX_TARGET)
  while(sem_trywait(&mStopStream) == 0);
  while(sem_trywait(&mStreamStartStop) == 0);
#endif

#if defined(_WINDOWS)
  //------------------------------------------------------------------
  // Start the stream - WINDOWS
  //------------------------------------------------------------------
  mpStreamThread = AfxBeginThread(::StartStreamThread, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);

  if(mpStreamThread == 0)                  //AfxBeginThread failed
  {
    return 2;
  }

  mpStreamThread->m_bAutoDelete = TRUE;    //This is the default
  mpStreamThread->ResumeThread();

#elif defined LINUX_TARGET
  //------------------------------------------------------------------
  // Start the stream - LINUX
  //------------------------------------------------------------------
  int rc = pthread_create(&mpStreamThread, NULL, ::StartStreamThread, this);
  
  if(rc != 0)
  {
    return 2;
  }
#endif

  if(mpStreamThread == 0)                  //StreamThread shut itself down
  {
    return 2;
  }

  return 0;
}

int CUSBCommunicationsInterface::StreamThread()
//=====================================================================
// StreamThread
//=====================================================================
{
#if defined(_WINDOWS)
  DWORD   dwResult;
#elif defined LINUX_TARGET
  int rc;
#endif

  std::map<void*, bool>::iterator map_it;
  std::list<unsigned char *>::iterator list_it;
  struct libusb_transfer *xfr = NULL;

  struct timeval zero_tv;
  zero_tv.tv_sec = 0;
  zero_tv.tv_usec = 0;

  mPendingIO = 0;
  mPendingTransfer = 0;
  mDataList.clear();

  //Allocate buffers
  mAvailableBuffers.clear();
  for(int i=0; i<MAX_PEND_XFERS; i++)
  {
    unsigned char * data = (unsigned char*)malloc(MAX_XFER_SIZE);
    mAvailableBuffers.push_back(data);
  }

  //Allocate transfers
  mTransfers.clear();
  for(int i=0; i<MAX_PEND_IO; i++)
  {
     xfr = libusb_alloc_transfer(0);
     mTransfers[(void*)xfr] = false;
  }

  mPartialDataItem = NULL;
  mPartialDataIndex = 0;

  //--------------
  // We are active
  //--------------
  mStreamState = 1;

#if defined(_WINDOWS)
  mStreamStartStop.SetEvent();     //Alert caller
#elif defined LINUX_TARGET
  sem_post(&mStreamStartStop);
#endif

  bool stopMessageReceived;

  while(1)
  {
    //-------------------------------------
    // Check for STOP STREAM
    //-------------------------------------
    stopMessageReceived = FALSE;

#if defined (_WINDOWS)
    dwResult = ::WaitForSingleObject(mStopStream.m_hObject, 0);
    if(dwResult == WAIT_OBJECT_0)
    {
      stopMessageReceived = TRUE;
    }

#elif defined LINUX_TARGET
    while((rc=sem_trywait(&mStopStream))==-1 && errno==EINTR);
    if(rc == 0)
    {
      stopMessageReceived = TRUE;
    }
#endif

    if(stopMessageReceived)
    {
      // Cancel any pending transfer requests:
      GetDataAccess();
      {
        for(map_it = mTransfers.begin(); map_it != mTransfers.end(); map_it++)
        {
          if(map_it->second == true)
          {
            xfr = (struct libusb_transfer*)(map_it->first);
            int err = libusb_cancel_transfer(xfr);
            if(mPrintTraceMessages)
            {
              if(err)
              {
                TRACE("Cancel Transfer Error = %d\n", err);
              }
              else
              {
                TRACE("Transfer Cancelled\n");
              }
            }
          }
        }
      }
      ReleaseDataAccess();
    }

    if(libusb_handle_events_timeout_completed(NULL, &zero_tv, NULL) != LIBUSB_SUCCESS) 
    {
      break;
    }

    if( (mStreamState == 1) && (mPendingIO < MAX_PEND_IO) && (mPendingTransfer < MAX_PEND_XFERS) )
    {
      // Submit new transfer
      SubmitTransfer();
    }

    if( (mStreamState == 2) && (mPendingIO == 0) )
    {
      // Streaming Cancelled and pendingIO cancelled
      break;
    }

    //Sleep(0);

  }

  // Free memory
  GetDataAccess();
  {
    while(mDataList.size() > 0)
    {
      DataItem * item = mDataList.back();
      free((void*)(item->data));
      delete item;
      mDataList.pop_back();
    }

    for(map_it = mTransfers.begin(); map_it != mTransfers.end(); map_it++)
    {
      xfr = (struct libusb_transfer*)(map_it->first);
      libusb_free_transfer(xfr);
    }

    for(list_it = mAvailableBuffers.begin(); list_it != mAvailableBuffers.end(); list_it++)
    {
      free(*list_it);
    }
    mAvailableBuffers.clear();
  }
  ReleaseDataAccess();

  if(mPartialDataItem != NULL)
  {
    free(mPartialDataItem->data);
    delete mPartialDataItem;
    mPartialDataItem = NULL;
  }

  mStreamState        = 0;
  mpStreamThread      = 0;

#if defined(_WINDOWS)
  mStreamStartStop.SetEvent();
#elif defined LINUX_TARGET
  sem_post(&mStreamStartStop);
#endif

  return 0;
}

void CallbackUSBTransferCompleteHelper(struct libusb_transfer *xfr)
//=====================================================================
// callbackUSBTransferCompleteHelper -- Helper function for callback
//=====================================================================
{
  if(xfr)
  {
    ((CUSBCommunicationsInterface *)(xfr->user_data))->CallbackUSBTransferComplete(xfr);
  }
}

void CUSBCommunicationsInterface::SubmitTransfer(void)
//=====================================================================
// SubmitTransfer -- Submit a transfer request to libusb
//=====================================================================
{
  struct libusb_transfer *xfr = NULL;
  unsigned char * data = NULL;
  std::map<void*, bool>::iterator it;

  // Find available xfr structure & data buffer
  GetDataAccess();
  {
    for(it = mTransfers.begin(); it != mTransfers.end(); it++)
    {
      if(it->second == false)
      {
        xfr = (struct libusb_transfer*)(it->first);
      }
    }

    if(mAvailableBuffers.size() > 0)
    {
        data = mAvailableBuffers.front();
    }

    if( (xfr != NULL) && (data != NULL) )
    {
        mTransfers[(void*)xfr] = true;
        mAvailableBuffers.pop_front();
    }
  }
  ReleaseDataAccess();

  // Configure transfer structure
  if( (xfr != NULL) && (data != NULL) )
  {
    libusb_fill_bulk_transfer(xfr, mHandle, STREAM_EP_READ, 
                            data, MAX_XFER_SIZE, 
							(libusb_transfer_cb_fn)(::CallbackUSBTransferCompleteHelper), this,
                            0);

    if(libusb_submit_transfer(xfr) < 0)
    {
      // Error
      GetDataAccess();
      {
        mTransfers[(void*)xfr] = false;
        mAvailableBuffers.push_back(data);
      }
      ReleaseDataAccess();

      if(mPrintTraceMessages)
      {
        TRACE("Transfer Submit Failed\n");
      }
    }
    else
    {
      ++mPendingIO;
      ++mPendingTransfer;
      //TRACE("Transfer Submitted, pendingIO=%d, pendingTransfer=%d\n", mPendingIO, mPendingTransfer);
    }
  }

}

void CUSBCommunicationsInterface::CallbackUSBTransferComplete(struct libusb_transfer *xfr)
{
  DataItem *newItem;

  switch(xfr->status)
  {
      case LIBUSB_TRANSFER_COMPLETED:
        // Success here, data transfered are inside xfr->buffer
        // and the length is xfr->actual_length
        newItem = new DataItem;
        newItem->size = xfr->actual_length;
        newItem->data = xfr->buffer;

        GetDataAccess();
        {
          mDataList.push_front(newItem);
          mTransfers[(void*)xfr] = false;
        }
        ReleaseDataAccess();

        --mPendingIO;

        //TRACE("Transfer Completed, length=%d, pendingIO=%d, pendingTransfer=%d\n", newItem->size, mPendingIO, mPendingTransfer);

        if( (mPendingIO < MAX_PEND_IO) && (mPendingTransfer < MAX_PEND_XFERS) )
        {
          // Submit new transfer
          SubmitTransfer();
        }

        break;
      case LIBUSB_TRANSFER_CANCELLED:
      case LIBUSB_TRANSFER_NO_DEVICE:
      case LIBUSB_TRANSFER_TIMED_OUT:
      case LIBUSB_TRANSFER_ERROR:
      case LIBUSB_TRANSFER_STALL:
      case LIBUSB_TRANSFER_OVERFLOW:
        //libusb_free_transfer(xfr);
        GetDataAccess();
        {
          mTransfers[(void*)xfr] = false;
          mAvailableBuffers.push_back(xfr->buffer);
        }
        ReleaseDataAccess();

        --mPendingIO;
        
        if(mPrintTraceMessages)
        {
          TRACE("LIBUSB ERROR = %d, PendingIO=%d\n", xfr->status, mPendingIO);
        }
        break;
  }
}

//****
// TODO: header
// returns 0=success
//         1=no more items
//****
int CUSBCommunicationsInterface::StreamRead(unsigned char * buffer, unsigned int length, unsigned int * transferredLength)
{
  int list_size;

  if(!buffer)
    return FALSE;

  *transferredLength = 0;

  // We keep a pointer to the last buffer if it wasn't fully emptied
  // Check it first
  if(mPartialDataItem != NULL)
  {
    if(length >= (mPartialDataItem->size - mPartialDataIndex))    //Buffer to fill is larger than data remaining
    {
      // Copy the rest of the buffer
      memcpy(buffer, &((mPartialDataItem->data)[mPartialDataIndex]), (mPartialDataItem->size - mPartialDataIndex));
      *transferredLength += (mPartialDataItem->size - mPartialDataIndex);

      GetDataAccess();
      {
        mAvailableBuffers.push_back(mPartialDataItem->data);
      }
      ReleaseDataAccess();

      delete mPartialDataItem;
      mPartialDataItem = NULL;
    }
    else                                          //Can only fit some of the data in this buffer
    {
      // Copy some of the buffer
      memcpy(buffer, &((mPartialDataItem->data)[mPartialDataIndex]), length);
      mPartialDataIndex += length;

      // Return
      *transferredLength = length;
      return 0;
    }
  }

  GetDataAccess();
  {
    list_size = mDataList.size();
  }
  ReleaseDataAccess();

  if(list_size < 1)
  {
    if(*transferredLength == 0)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }

  //Iterate through list getting as much data as we can:
  do
  {
    GetDataAccess();
    {
      mPartialDataItem = mDataList.back();
      mPartialDataIndex = 0;
      mDataList.pop_back();
      --mPendingTransfer;
      list_size = mDataList.size();
    }
    ReleaseDataAccess();

    //TRACE("Transfer Retrieved, pendingIO=%d, pendingTransfer=%d\n", mPendingIO, mPendingTransfer);

    if((length - *transferredLength) >= mPartialDataItem->size)
    {
      // Copy all of the buffer
      memcpy(&buffer[*transferredLength], mPartialDataItem->data, mPartialDataItem->size);
      *transferredLength += mPartialDataItem->size;
      
      GetDataAccess();
      {
        mAvailableBuffers.push_back(mPartialDataItem->data);
      }
      ReleaseDataAccess();

      delete mPartialDataItem;
      mPartialDataItem = NULL;
    }
    else
    {
      // Copy some of the buffer
      memcpy(&buffer[*transferredLength], mPartialDataItem->data, (length - *transferredLength));
      mPartialDataIndex += (length - *transferredLength);
      *transferredLength += (length - *transferredLength);
      return 0;
    }
  }
  while(list_size > 0);

  return 0;
  
}

void CUSBCommunicationsInterface::StopStream(void)
//=====================================================================
// StopStream - Stop the stream and free resources.
//
//=====================================================================
{
  //-------------------------
  // Stop Streaming - WINDOWS
  //-------------------------
#if defined _WINDOWS
  if(mpStreamThread != 0)
  {
    if(mPrintTraceMessages)
    {
      TRACE("Set Stop Event\n");
    }

    mStreamState = 2;

    mStreamStartStop.ResetEvent();
    mStopStream.SetEvent();

    if(!mStreamStartStop.Lock(5000))
    {
      if(mPrintTraceMessages)
      {
        TRACE("Timeout Waiting For libusb Stream to Stop\n");
      }
    }
  }
  else
  {
    mStopStream.SetEvent();        //Just in case thread is really active
  }

#elif defined LINUX_TARGET
  if(mpStreamThread != 0)
  {
	mStreamState = 2;

    while(sem_trywait(&mStreamStartStop) == 0);

    sem_post(&mStopStream);

    pthread_join(mpStreamThread, NULL);
  }
  else
  {
    sem_post(&mStopStream);       //Just in case thread is really active
  }
#endif
}

void CUSBCommunicationsInterface::GetDataAccess()
//=====================================================================
//=====================================================================
{
#if defined(_WINDOWS)
  mCS.Lock();
#elif defined(LINUX_TARGET)
  pthread_mutex_lock(&mCS);
#endif
}

void CUSBCommunicationsInterface::ReleaseDataAccess()
//=====================================================================
//=====================================================================
{
#if defined(_WINDOWS)
  mCS.Unlock();
#elif defined(LINUX_TARGET)
  pthread_mutex_unlock(&mCS);
#endif
}

#endif
USBCommunicationsInterface.h (text/plain, 5.4 KB)
/////////////////////////////////////////////////////////////////////////////
// USBCommunicationsInterface.h : header file
#pragma once

#if defined(_WINDOWS)
  #define USE_LIBUSBK //LibusbK is Windows Only
#else
#endif

#ifdef USE_LIBUSBK
  #include "mti_lib/third_party/libusbk/includes/libusbk.h"
#else
#if defined(_WINDOWS)
  #include "mti_lib/third_party/libusb-1.0/include/libusb-1.0/libusb.h"
#elif defined(LINUX_TARGET)
  #include "/usr/include/libusb-1.0/libusb.h"
#endif
#endif

#if defined LINUX_TARGET
 #include <pthread.h>
 #include <semaphore.h>
#endif

#include <list>
#include <map>


////////////////////////////////////////////////////////////////////////////
// DECLARATIONS -- USB IDs
////////////////////////////////////////////////////////////////////////////
#define MTI_VID            0x19FD

#define DACC_PID           0x0002           //Digital Accumeasure
#define HERMES_PID         0x0003           //Hermes project (digital control of 9000 card)
#define MT4_PID            0x0004           //Digital Microtrak

struct DataItem
{
  unsigned int size;
  unsigned char *data;
};


/////////////////////////////////////////////////////////////////////////////
// class CUSBCommunicationsInterface
/////////////////////////////////////////////////////////////////////////////
class CUSBCommunicationsInterface
{
  friend void CallbackUSBTransferCompleteHelper(struct libusb_transfer *xfr);

#if defined _WINDOWS
  friend UINT StartStreamThread(void *pObject);
#elif defined LINUX_TARGET
  friend void* StartStreamThread(void *pObject);
#endif

public:
   CUSBCommunicationsInterface(void);
  ~CUSBCommunicationsInterface(void);

  void      SetVID(int vid)                       {mVID = vid;}
  void      SetPID(int pid)                       {mPID = pid;}

  void      SetTraceOutput(BOOL onoff)            {mPrintTraceMessages = onoff;}

  int       GetVID()            const             {return mVID;}
  int       GetPID()            const             {return mPID;}

  int       FindAllDevices(int maxDevices);

  int       GetDeviceIndex( CString name );

  CString   GetDeviceName(            int index );
  CString   GetDeviceModelNumber(     int index );
  CString   GetDeviceSerialNumber(    int index );
  CString   GetDeviceSoftwareVersion( int index );

  int       ConnectToDevice( CString name );
  int       ConnectToDevice( int index );

  void      Disconnect(void);

  bool      IsConnected(void);
  bool      ReadyToStream(void);

  int       WriteCommand(const char *buffer, int bytesToWrite, int *bytesWritten);
  int       ReadResponse(char *buffer, int maxBytesToRead, int *bytesRead, int tmo);

  int       ReadStreaming(char *buffer, int minBytesToRead, int maxBytesToRead, int *bytesRead, int tmo);

  int       FlushCommand(void);

private:
  int       StartStream(void);
  void      StopStream(void);
  int       ReadCommand(char *buffer, int maxBytesToRead, int *bytesRead, int tmo);
  int       FlushStreaming(void);

private:
  int     mDeviceIndex;                 //Index of device we are currently connected to

  //-------------------------------------
  // USB Communication controls
  //-------------------------------------
#ifdef USE_LIBUSBK
  KUSB_DRIVER_API      mKUsb;           //libusbK
  KLST_HANDLE          mKDeviceList;    //libusbK
  KUSB_HANDLE          mKHandle;        //libusbK
  KSTM_HANDLE          mKStmHandle;     //libusbK

  KLST_DEVINFO_HANDLE *mpKDeviceList;   //List of USB devices that match vid & pid (used by libusbK)
#else
  libusb_device_handle *mHandle;
  int                   mPendingIO;
  int                   mPendingTransfer;

  DataItem             *mPartialDataItem;   //A data item that has been partially returned via StreamRead
  unsigned int          mPartialDataIndex;  //The index of the next unread location in the buffer

  std::list<DataItem *> mDataList;
  std::list<unsigned char *> mAvailableBuffers;
  std::map<void*, bool> mTransfers;        //true when used

  void      GetDataAccess();
  void      ReleaseDataAccess();

#if defined(_WINDOWS)
  CCriticalSection     mCS;
  CWinThread          *mpStreamThread;
  CEvent               mStopStream;          //Request to stop the stream
  CEvent               mStreamStartStop;     //Stream has started/stopped
#elif defined(LINUX_TARGET)
  pthread_mutex_t      mCS;
  pthread_t            mpStreamThread;
  sem_t                mStopStream;
  sem_t                mStreamStartStop;
#endif

  int        mStreamState;         //0=Idle  1=Active  2=Terminating

  int        StreamThread(void);

  void       SubmitTransfer(void);
  void       CallbackUSBTransferComplete(struct libusb_transfer *xfr);

  int        StreamRead(unsigned char * buffer, unsigned int length, unsigned int * transferredLength);
#endif

  CString             *mpDeviceNames;     //List of USB device names
  CString             *mpDeviceModel;     //List of USB device models
  CString             *mpDeviceSerial;    //List of USB device serial numbers
  CString             *mpDeviceVersion;   //List of USB device SW versions
  int                  mDeviceCount;      //Number of USB devices found

  int                  mVID;              //Vendor ID   (MTII = 0x19FD)
  int                  mPID;              //Product ID  (DACC = 0x0002)

  BOOL                 mPrintTraceMessages;
};