/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: Quinn-CE-USB-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW
 *****************************************************************************
 * $Workfile: O:/mwohler_z_ms-d/z_ms-d/src/working/Quinn/HcUsbDrv/UsbHandling.cpp $
 * $Revision: /main/ms-d/54 $
 * $Date: 2014-04-03 13:45:51 $
 * $Author: mwohler $
 *****************************************************************************
 * Target:
 *  Microsoft Windows CE 5.0
 *****************************************************************************
 * Description:
 *  Quinn-USB-Driver: Implementation of the class CUsbHandling for
 *  general USB handling
 *****************************************************************************
 *
 * $Log: O:/mwohler_z_ms-d/z_ms-d/src/working/Quinn/HcUsbDrv/UsbHandling.cpp$ 
 * 
 * /main/ms-d/54   2014-04-03 13:45:51   mwohler 
 * Enumerate multiple rmnet interfaces properly.
Added support for PLSx Rel. 2.
 
 * 
 * /main/ms-d/53   2012-11-08 13:03:56   thinze 
 * bugfix - deadlock when attaching failed and app is opening port 
 * 
 * /main/ms-d/52   2012-10-08 17:00:00   thinze 
 * intialize dwResumeDelay (may be not initialized if netiher Siemens nor Citnerion registry key is available) 
 * 
 * /main/ms-d/51   2012-07-18 15:28:26   thinze 
 * added support for CDC-ECM 
 * 
 * /main/ms-d/50   2011-02-14 14:14:34   ctna0152 
 * fix for IPIS100069778
 * removed USE_DEVICE_REQUEST_REDIRECT encapsulation 
 * 
 * /main/ms-d/49   2011-02-02 10:27:51   ctna0450 
 * Removed unused USB-Reset workaround for Uranus USB problems 
 * 
 * /main/ms-d/48   2010-12-14 15:16:03   ctna0152 
 * - usb reset cleanup
 * - changed debug messages 
 * 
 * /main/ms-d/47   2010-12-13 14:22:20   ctna0152 
 * added USB reset handling 
 * 
 * /main/ms-d/46   2010-11-15 17:50:54   ctna0152 
 * added workaround "PAUSE_ON_RESUME" for IPIS100068602 
 * 
 * /main/ms-d/45   2010-08-27 09:44:48   ctna0450 
 * Added workaround for changed USB_INTERFACE descriptor at CE7
 *
 * /main/ms-d/44   2010-08-24 12:02:22   ctna0450
 * Fixed possible usage of wrong registry path
 *
 * /main/ms-d/42   2010-08-17 14:40:14   ctna0450
 * Use compiler flag for "attach before detach" problem to reenable capability for more than one instance handling
 *
 * /main/ms-d/41   2010-08-09 11:33:12   ctna0450
 * Added manual device index assignment
 *
 * /main/ms-d/40   2010-08-04 13:54:57   ctna0450
 * Unified enumeration process
 *
 * /main/ms-d/39   2009-03-04 10:08:37   ctna0450
 * Removed company name from m_hDetachEvent
 *
 * /main/ms-d/38   2009-02-18 11:49:36   ctna0256
 * Set copyright text
 *
 * /main/ms-d/37   2008-08-20 11:39:32   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/36   2008-06-19 14:23:42   ht37010
 * added Cinterion Vendor ID handling
 *
 * /main/ms-d/35   2008-05-16 14:47:56   ht37010
 * added support for MC75i
 * todo: redesign creating of virtual ports without querying the product ID
 *
 * /main/ms-d/34   2008-04-21 14:38:44   ht37010
 * bugfix: delete critical section in desctructor
 *
 * /main/ms-d/33   2008-04-11 19:10:04   ht37010
 * added debug msgs for ZONE_USB_HOSTFN_ERROR
 *
 * /main/ms-d/32   2007-12-18 15:26:59   ht37010
 * changed debug messages
 *
 * /main/ms-d/31   2007-12-18 14:59:42   ht37010
 *
 * /main/ms-d/30   2007-12-18 14:55:28   ht37010
 * changed debug messages
 *
 * /main/ms-d/29   2007-12-18 14:07:26   ht37010
 * changed debug messages
 *
 * /main/ms-d/28   2007-12-17 16:42:49   ht37010
 * added debug messages
 *
 * /main/ms-d/27   2007-09-12 12:52:20   ht37010
 * added debug messages
 * added encapsulation for device request handling
 *
 * /main/ms-d/26   2007-09-07 15:08:44   ht37010
 * improved device request handling
 *
 * /main/ms-d/25   2007-08-31 13:48:26   ht37010
 * bugfix for IPIS40691:
 * deinitialize SelectiveSuspend first to unblock any threads waiting for usb device resume
 *
 * /main/ms-d/24   2007-08-28 12:51:16   ht37010
 * added debug messages
 *
 * /main/ms-d/23   2007-08-14 12:43:54   ht37010
 * added support for all modules
 * added debug messages
 *
 * /main/ms-d/22   2007-08-01 10:32:21   ht37010
 * improved handling if new device is attached before old device is detached during global resume
 *
 * /main/ms-d/21   2007-06-01 20:39:36   ht37010
 * synchronized system function calls for USB
 *
 * /main/ms-d/20   2007-05-30 15:26:32   ht37010
 * added debug messages
 *
 * /main/ms-d/19   2007-05-08 10:58:56   ht37010
 * use correct length for output buffer
 *
 * /main/ms-d/18   2007-02-02 15:20:25   wm36264
 * Block USB attach while detach is in progress
 *
 * /main/ms-d/17   2006-12-19 17:49:03   wm36264
 * Tidy up unused USB resources
 *
 * /main/ms-d/16   2006-12-18 16:45:43   wm36264
 * Create ndis port at last in case we haven'r enough pipes
 *
 * /main/ms-d/15   2006-12-14 12:15:49   wm36264
 * Prepared resume notification
 *
 * /main/ms-d/14   2006-11-03 17:07:13   wm36264
 * Continued to prepare selective suspend handling
 *
 * /main/ms-d/13   2006-11-02 12:08:05   wm36264
 * Support more than one Siemens product id at the same time
 *
 * /main/ms-d/12   2006-10-20 13:38:12   wm36264
 * Addapted to latest USB modifications in module
 *
 * /main/ms-d/11   2006-10-20 12:16:18   wm36264
 * The ndis irq pipe notifies us when there is qmi data available on the default pipe
 *
 * /main/ms-d/10   2006-10-18 12:33:59   wm36264
 * Several modifications regarding default pipe handling for qmi
 *
 * /main/ms-d/9   2006-10-12 16:52:52   wm36264
 * For a possible future selective suspend implementation it is much easier when there is only one pipe class
 *
 * /main/ms-d/8   2006-10-10 15:14:27   wm36264
 * Prepared usage of ndis interface number
 *
 * /main/ms-d/7   2006-10-09 15:24:15   wm36264
 * Fixed USB detach problems
 *
 * /main/ms-d/6   2006-10-06 18:00:37   wm36264
 * Make virtual ports fully instancable and use different device names for serial, ndis and qmi devices
 *
 * /main/ms-d/5   2006-09-29 15:33:10   wm36264
 * Consider latest findings about QMI and NDIS handling
 *
 * /main/ms-d/4   2006-09-25 18:05:48   wm36264
 * Properly check required resources
 *
 * /main/ms-d/3   2006-09-22 14:33:32   wm36264
 * Cosmetics mostly
 *
 * /main/ms-d/2   2006-09-21 17:05:53   wm36264
 * Prepared access to QMI interface
 *
 * /main/ms-d/1   2006-09-18 15:31:49   wm36264
 * Initial Quinn Windows CE USB Driver based on UsbSer
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Cinterion Wireless Modules GmbH
// Copyright (C) 2006-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// UsbHandling.cpp
//
// This file contains the implementation of the class "CUsbHandling" which
// implements the access to the USB interface.
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////

BOOL WINAPI USBDeviceNotify(LPVOID lpvNotifyParameter, DWORD dwCode, LPDWORD* dwInfo1, LPDWORD* dwInfo2, LPDWORD* dwInfo3, LPDWORD* dwInfo4);

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
//  local variables
//
//////////////////////////////////////////////////////////////////////////////

CRITICAL_SECTION CUsbHandling::m_csSystemLock;

//////////////////////////////////////////////////////////////////////////////
//
// Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Initialize the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CUsbHandling::InitSystemLock( void ) {
  DEBUGMSG(ZONE_INIT|ZONE_LOCK, (TEXT("HcUsb: +CUsbHandling::InitSystemLock()\r\n")));
  InitializeCriticalSection( &m_csSystemLock );
  DEBUGMSG(ZONE_INIT|ZONE_LOCK, (TEXT("HcUsb: -CUsbHandling::InitSystemLock()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Deinitialize the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CUsbHandling::DeinitSystemLock( void ) {
  DEBUGMSG(ZONE_INIT|ZONE_LOCK, (TEXT("HcUsb: +CUsbHandling::DeinitSystemLock()\r\n")));
  DeleteCriticalSection( &m_csSystemLock );
  DEBUGMSG(ZONE_INIT|ZONE_LOCK, (TEXT("HcUsb: -CUsbHandling::DeinitSystemLock()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Enter the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CUsbHandling::LockSystem( void ) {
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: +CUsbHandling::LockSystem()\r\n")));
  EnterCriticalSection( &m_csSystemLock );
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: -CUsbHandling::LockSystem()\r\n")));
}

//////////////////////////////////////////////////////////////////////////////
//
// Leave the critical section used to synchronize system functions.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
void CUsbHandling::UnlockSystem( void ) {
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: +CUsbHandling::UnlockSystem()\r\n")));
  LeaveCriticalSection( &m_csSystemLock );
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb: -CUsbHandling::UnlockSystem()\r\n")));
}


//////////////////////////////////////////////////////////////////////////////
//
// Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
// Parameters:
// None.
//
//////////////////////////////////////////////////////////////////////////////
CUsbHandling::CUsbHandling() {
  InitializeCriticalSection(&m_csLock);
  m_hDetachEvent = CreateEvent(NULL, TRUE, TRUE, TEXT("HcUsbDrvDetachEvent"));
  m_hDevice = NULL;
  m_pFuncs = NULL;
  m_fUsbAttached = FALSE;
  m_dwOpenPorts = 0;
  memset(m_hStreamDevices, 0, sizeof(m_hStreamDevices));
  m_hPortsClosedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hIfaceReadyEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

  memset(m_pVirtualPorts, 0, sizeof(m_pVirtualPorts));
  InitializeCriticalSection(&m_csDeviceRequestLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CUsbHandling::~CUsbHandling() {
  USBDeviceDetach();
  CloseHandle(m_hIfaceReadyEvent);
  CloseHandle(m_hPortsClosedEvent);
  CloseHandle(m_hDetachEvent);
  DeleteCriticalSection(&m_csLock);
  DeleteCriticalSection(&m_csDeviceRequestLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Called directly from the driver's interface USBDeviceAttach() function
// with the parameters necessary to initialize our USB handling.
//
// Parameters
// Device      : Handle to our USB device.
// Funcs       : Pointer to a USB device interface function table.
// dwClientInfo: Value saved in the registry value "ClientInfo" by call to
//               ActivateDevice().
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::USBDeviceAttach(USB_HANDLE Device, LPCUSB_FUNCS Funcs, DWORD dwClientInfo) {
  SYNCBLOCK(m_csLock);

  // Wait until running detach processes are finished
  WaitForSingleObject(m_hDetachEvent, INFINITE);

  // First check the given parameters and required resources
  if (!Device) return FALSE;
  if (!Funcs) return FALSE;
  if (!m_hIfaceReadyEvent) return FALSE;
  if (!m_hPortsClosedEvent) return FALSE;

  // Check if we aren't already attached and all required resources are available
  if (m_fUsbAttached) return FALSE;
  m_hDevice = Device;
  m_pFuncs = Funcs;
  m_fUsbAttached = TRUE;

  // Now look for a device & interface to control
  if (!ParseDescriptors(dwClientInfo)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to find a fitting USB interface!\r\n")));
    UNSYNCBLOCK(m_csLock);
    USBDeviceDetach();
    return FALSE;
  }

  // Register our USBDeviceNotify callback
  if (!Funcs->lpRegisterNotificationRoutine(Device, USBDeviceNotify, (LPVOID)dwClientInfo)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: RegisterNotificationRoutine() error: %d\r\n"), GetLastError()));
    UNSYNCBLOCK(m_csLock);
    USBDeviceDetach();
    return FALSE;
  }

  // Kick start the selective suspend handling
  if (!m_SelectiveSuspend.DeviceAccessed()) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to start selective suspend handling\r\n")));
    UNSYNCBLOCK(m_csLock);
    USBDeviceDetach();
    return FALSE;
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Invalidate registrations and allocations made by USBDeviceAttach().
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::USBDeviceDetach(void) {
BOOL fRet = TRUE;

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: +CUsbHandling::USBDeviceDetach()\r\n")));

  ResetEvent(m_hDetachEvent);

  // First invalidate the USB attached flag and abort possibly still running initialization
  EnterCriticalSection(&m_csLock);
  m_fUsbAttached = FALSE;
  SetEvent(m_hIfaceReadyEvent);
  LeaveCriticalSection(&m_csLock);

  // Deinitialize the selective suspend handling
  if (m_SelectiveSuspend.Deinitialize() == FALSE) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach(): Failed to Deinitialize Selective Suspend\r\n")));
  }
  // Now wait until all port instances are closed
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach(): Wait until all virtual port instances are closed\r\n")));

  for ( DWORD i = 0; i < MAX_VIRTUAL_PORTS; i++ ) {
    if ( m_pVirtualPorts[i] != NULL ) {
      if (m_pVirtualPorts[i]->GetUsbHandling() == this) {
        m_pVirtualPorts[i]->EnsurePortClosed();
      }
      else {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach() - port used by other usb handling \r\n"), i));
      }
    }
  }

  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  USBDeviceDetach() - sync: +CUsbHandling\r\n")));
  SYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_LOCK, (TEXT("HcUsb:  USBDeviceDetach() - sync: -CUsbHandling\r\n")));

  for ( DWORD i = 0; i < MAX_VIRTUAL_PORTS; i++ ) {
    if ( m_pVirtualPorts[i] != NULL ) {
      if (m_pVirtualPorts[i]->GetUsbHandling() == this) {
        HANDLE hStreamDevice = m_pVirtualPorts[i]->GetStreamHandle();
        if (hStreamDevice != NULL) {
          if (!DeactivateDevice(hStreamDevice)) {
            DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach() failed on port %i \r\n"), i));
            fRet = FALSE;
          }
        }
        else {
          DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach() - port w/o stream handle \r\n"), i));
        }
      }
      else {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach() - port used by other usb handling \r\n"), i));
      }
    }
  }

  if (!m_DefaultPipe.Deinitialize()) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::USBDeviceDetach(): Failed to tidy up default pipe\r\n")));
    fRet = FALSE;
  }

  m_hDevice = NULL;
  m_pFuncs = NULL;

  SetEvent(m_hDetachEvent);

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::USBDeviceDetach()\r\n")));
  return fRet;
}

 //////////////////////////////////////////////////////////////////////////////
//
// Initialize USB interface of an created virula port device. This
// function has to be called by the corresponding device from the XXX_Init()
// call initiated by the ActivateDevice() call. The execution of the USB
// attach blocks until this has happened.
//
// Parameters:
// pVirtualPort: Pointer to the intance of the corresponding virtual port.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::InitUsbInterface(CVirtualPort* pVirtualPort) {
LPCUSB_ENDPOINT  pEndpoint;
DWORD            i;
BOOL             fRet = TRUE;

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: +CUsbHandling::InitUsbInterface()\r\n")));
  SYNCBLOCK(m_csLock);

  if (!pVirtualPort || !m_fUsbAttached) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: -InitUsbInterface(FALSE:ptr=%p;att=%d)\r\n"), pVirtualPort, m_fUsbAttached));
    return FALSE;
  }

  if ((pVirtualPort->GetDeviceType() == HC_SERIAL) && m_pCurrCdcCtrlIface && m_pCurrCdcDataIface) {
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  InitUsbInterface() - init CDC-ACM\r\n")));
    // scan the endpoints of the ctrl and data interface for our required pipes
    // CTRL interface will be scan at last so it will set as interface for this VirtualPort (used in vendor-transfers)
    LPCUSB_INTERFACE pIface[] = {m_pCurrCdcDataIface, m_pCurrCdcCtrlIface};
    for ( int j=0; j < 2; j++ ) {
      m_pCurrUsbIface = pIface[j];
      DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: charlie: parse %d EPs\r\n"), m_pCurrUsbIface->Descriptor.bNumEndpoints));
      for (i = 0; i < m_pCurrUsbIface->Descriptor.bNumEndpoints; i++) {
        pEndpoint = m_pCurrUsbIface->lpEndpoints + i;
        DUMP_USB_ENDPOINT_DESCRIPTOR(pEndpoint->Descriptor);
        if (USB_ENDPOINT_DIRECTION_OUT(pEndpoint->Descriptor.bEndpointAddress)) {
          if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
            // Initialize the bulk out pipe
            if (!pVirtualPort->GetBulkOutPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
              DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the bulk out pipe\r\n")));
              fRet = FALSE;
              break;
            }
          }
        } else if (USB_ENDPOINT_DIRECTION_IN(pEndpoint->Descriptor.bEndpointAddress)) {
          if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
            // Initialize the bulk in pipe
            if (!pVirtualPort->GetBulkInPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
              DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the bulk in pipe\r\n")));
              fRet = FALSE;
              break;
            }
          }
          if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT) {
            // Initialize the interrupt in pipe
            if (!pVirtualPort->GetIrqInPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
              DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the interrupt in pipe\r\n")));
              fRet = FALSE;
              break;
            }
          }
        } else {
          DEBUGMSG(ZONE_ERROR, (TEXT("HcUsb: Unsupported Endpoint: 0x%x\r\n"), pEndpoint->Descriptor.bEndpointAddress));
          fRet = FALSE;
          break;
        }
      }
    }
  } else if (pVirtualPort->GetDeviceType() != HC_CMD) {
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  InitUsbInterface() - init vendor specific (%s)\r\n"), pVirtualPort->GetDeviceType() == HC_SERIAL ? TEXT("SERIAL") : TEXT("NDIS")));
    // Scan the endpoints for our required pipes
    for (i = 0; i < m_pCurrUsbIface->Descriptor.bNumEndpoints; i++) {
      pEndpoint = m_pCurrUsbIface->lpEndpoints + i;

      DUMP_USB_ENDPOINT_DESCRIPTOR(pEndpoint->Descriptor);

      if (USB_ENDPOINT_DIRECTION_OUT(pEndpoint->Descriptor.bEndpointAddress)) {
        if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
          // Initialize the bulk out pipe
          if (!pVirtualPort->GetBulkOutPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
            DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the bulk out pipe\r\n")));
            fRet = FALSE;
            break;
          }
        }
      } else if (USB_ENDPOINT_DIRECTION_IN(pEndpoint->Descriptor.bEndpointAddress)) {
        if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
          // Initialize the bulk in pipe
          if (!pVirtualPort->GetBulkInPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
            DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the bulk in pipe\r\n")));
            fRet = FALSE;
            break;
          }
        }
        if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT) {
          // The default pipe of the ndis port belongs to our virtual command port
          if (pVirtualPort->GetDeviceType() != HC_NDIS) {
            // Initialize the interrupt in pipe
            if (!pVirtualPort->GetIrqInPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
              DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the interrupt in pipe\r\n")));
              fRet = FALSE;
              break;
            }
          } else {
            // Store the ndis interrupt pipe
            m_pEndointNdisIrqPipe = pEndpoint;
          }
        }
      } else {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Unsupported Endpoint: 0x%x\r\n"), pEndpoint->Descriptor.bEndpointAddress));
        fRet = FALSE;
        break;
      }
    }
    // if this is an CDC-ECM device than scan the endpoints of the data interface
    if (m_pCurrCdcDataIface != NULL)
    {
      DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  InitUsbInterface() - init CDC-ECM data interface\r\n")));
      for (i = 0; i < m_pCurrCdcDataIface->Descriptor.bNumEndpoints; i++) {
        pEndpoint = m_pCurrCdcDataIface->lpEndpoints + i;
        
        DUMP_USB_ENDPOINT_DESCRIPTOR(pEndpoint->Descriptor);
        
        if (USB_ENDPOINT_DIRECTION_OUT(pEndpoint->Descriptor.bEndpointAddress)) {
          if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
            // Initialize the bulk out pipe
            if (!pVirtualPort->GetBulkOutPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
              DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the bulk out pipe\r\n")));
              fRet = FALSE;
              break;
            }
          } else {
            DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Unsupported Endpoint OUT Type: 0x%x\r\n"), pEndpoint->Descriptor.bEndpointAddress));
            fRet = FALSE;
            break;
          }
        } else if (USB_ENDPOINT_DIRECTION_IN(pEndpoint->Descriptor.bEndpointAddress)) {
          if ((pEndpoint->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
            // Initialize the bulk in pipe
            if (!pVirtualPort->GetBulkInPipe()->Initialize(m_hDevice, m_pFuncs, pEndpoint, &m_SelectiveSuspend)) {
              DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the bulk in pipe\r\n")));
              fRet = FALSE;
              break;
            }
          } else {
             DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Unsupported Endpoint IN Type: 0x%x\r\n"), pEndpoint->Descriptor.bEndpointAddress));
             fRet = FALSE;
             break;
          }
        } else {
          DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Unsupported Endpoint Direction: 0x%x\r\n"), pEndpoint->Descriptor.bEndpointAddress));
          fRet = FALSE;
          break;
        }
      }
    }
  } else {
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  InitUsbInterface() - init vendor specific (CMD)\r\n")));
    if (m_pEndointNdisIrqPipe) {
      if (!pVirtualPort->GetIrqInPipe()->Initialize(m_hDevice, m_pFuncs, m_pEndointNdisIrqPipe, &m_SelectiveSuspend)) {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to initialize the ndis interrupt in pipe\r\n")));
        fRet = FALSE;
      }
    } else {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Cmd port initialization called without valid ndis port\r\n")));
      fRet = FALSE;
    }
  }

  if (fRet) {
    pVirtualPort->SetVirtualPortIndex(m_dwCurrVirtualPortIndex);
    pVirtualPort->SetUsbInterfaceNumber(m_bCurrInterfaceNum);
    pVirtualPort->SetUsbPortCaps(m_bCurrPortCaps);
    pVirtualPort->SetDefaultPipe(&m_DefaultPipe);
    m_pCurrVirtPort = pVirtualPort;
    AddVirtualport(pVirtualPort);
  } else {
    // Tidy up unused USB resources
    pVirtualPort->GetBulkOutPipe()->Deinitialize();
    pVirtualPort->GetBulkInPipe()->Deinitialize();
    pVirtualPort->GetIrqInPipe()->Deinitialize();
    if (pVirtualPort->GetDeviceType() == HC_NDIS) {
      m_pEndointNdisIrqPipe = NULL;
    }
    m_pCurrVirtPort = NULL;
  }

  SetEvent(m_hIfaceReadyEvent);

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::InitUsbInterface(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Increment our internal counter for open virtual ports. This function has
// to be called each time when a virtual port device has been opened.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::AddOpenedPort(void) {
  SYNCBLOCK(m_csLock);

  if (!m_fUsbAttached) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:  CUsbHandling::AddOpenedPort(): Tried to opened virtual port without USB attach\r\n")));
    return FALSE;
  }
  if (!m_dwOpenPorts) {
    ResetEvent(m_hPortsClosedEvent);
  }
  m_dwOpenPorts++;
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  CUsbHandling::AddOpenedPort(): Virtual port number %d opened\r\n"), m_dwOpenPorts));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Decrement our internal counter for open virtual ports. This function has
// to be called each time when a virtual port device has been closed.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::RemoveOpenedPort(void) {
  SYNCBLOCK(m_csLock);

  if (!m_dwOpenPorts) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::RemoveOpenedPort(): Tried to close virtual port with no one open\r\n")));
    return FALSE;
  }
  m_dwOpenPorts--;
  if (!m_dwOpenPorts) {
    SetEvent(m_hPortsClosedEvent);
  }
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: CUsbHandling::RemoveOpenedPort(): Virtual port closed, %d remain open\r\n"), m_dwOpenPorts));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Private implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Scan through the USB devices to find the fitting interface.
//
// Parameters:
// dwClientInfo: Value saved in the registry value "ClientInfo" by call to
//               ActivateDevice().
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::ParseDescriptors(DWORD dwClientInfo) {
LPCUSB_DEVICE     pDevice;
LPCUSB_INTERFACE  pInterface;
DWORD             dwNumInterfaces;
DWORD             i;
DWORD             dwSuspendDelay;
HKEY              hKey;
DWORD             dwRegDataSize;
#ifdef USE_WA_PAUSE_ON_RESUME
DWORD             dwResumeDelay = USB_DEF_RESUME_DELAY;
#endif

  // Get the device descriptors
  pDevice = m_pFuncs->lpGetDeviceInfo(m_hDevice);
  if (!pDevice) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: GetDeviceInfo error: %d\r\n"), GetLastError()));
    return FALSE;
  }

  DUMP_USB_DEVICE_DESCRIPTOR(pDevice->Descriptor);
  DUMP_USB_CONFIGURATION_DESCRIPTOR(pDevice->lpActiveConfig->Descriptor);

  // Get the proper configuration index
  for (i = 0; i < (DWORD)pDevice->Descriptor.bNumConfigurations; i++) {
    if (pDevice->lpActiveConfig == (pDevice->lpConfigs + i)) {
      DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found USB-ConfigIndex: %d\r\n"), i));
      break;
    }
  }

  pInterface       = pDevice->lpActiveConfig->lpInterfaces;
  dwNumInterfaces  = pDevice->lpActiveConfig->dwNumInterfaces;

  m_pEndointNdisIrqPipe = NULL;

  // Try to read suspend delay from registry
  dwSuspendDelay = USB_DEF_SUSPEND_DELAY;
  // 1st try the Cinterion registry key, then try the Siemens registry key
  if (((RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, 0, &hKey)) == ERROR_SUCCESS) ||
      ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY_COMPATIBLITY, 0, 0, &hKey) == ERROR_SUCCESS))) {
    dwRegDataSize = sizeof(DWORD);
    if ((RegQueryValueEx(hKey, USB_SUSPEND_DELAY_NAME, 0, NULL, (LPBYTE)(&dwSuspendDelay), &dwRegDataSize) != ERROR_SUCCESS)) {
      dwSuspendDelay = USB_DEF_SUSPEND_DELAY;
    }
    RegCloseKey(hKey);
  }

  if (dwSuspendDelay != INFINITE) {
#if defined(USB_DEVICE_SUSPENDED) && defined(USB_DEVICE_RESUMED)
  DEBUGMSG(ZONE_USB_SUSPEND_INFO, (TEXT("HcUsb: selective suspend - suspend delay = %d ms (notification mode)\r\n"), dwSuspendDelay));
#else
  DEBUGMSG(ZONE_USB_SUSPEND_INFO, (TEXT("HcUsb: selective suspend - suspend delay = %d ms (polling mode)\r\n"), dwSuspendDelay));
#endif
  }
  else {
    DEBUGMSG(ZONE_USB_SUSPEND_INFO, (TEXT("HcUsb: selective suspend - disabled\r\n")));
  }

  // And initialize the selective suspend handling
#ifdef USE_WA_PAUSE_ON_RESUME
  if (((RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, 0, &hKey)) == ERROR_SUCCESS) ||
      ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY_COMPATIBLITY, 0, 0, &hKey) == ERROR_SUCCESS))) {
    dwRegDataSize = sizeof(DWORD);
    if ((RegQueryValueEx(hKey, USB_PAUSE_ON_RESUME_DELAY, 0, NULL, (LPBYTE)(&dwResumeDelay), &dwRegDataSize) != ERROR_SUCCESS)) {
      dwResumeDelay = USB_DEF_RESUME_DELAY;
    }
    RegCloseKey(hKey);
  }
  m_SelectiveSuspend.SetPauseOnResume(dwResumeDelay);
#endif // USE_WA_PAUSE_ON_RESUME

  m_SelectiveSuspend.Initialize(m_hDevice, m_pFuncs, dwNumInterfaces, dwSuspendDelay);

  // We need the default pipe for command transfers
  if (!m_DefaultPipe.Initialize(m_hDevice, m_pFuncs, NULL, &m_SelectiveSuspend)) return FALSE;

  // First step through the existing interface to build a table with all existing and supported interfaces
  WORD wProductId = pDevice->Descriptor.idProduct;
  HC_DEVICE_INFO DevInfo[HC_MAX_USB_IFACES];
  memset(DevInfo, 0, sizeof(DevInfo));
  for (i = 0; i < HC_MAX_USB_IFACES; i++) {
    DevInfo[i].DevType = HC_UNKNOWN;
    DevInfo[i].bDataIndex = -1;
  }
  BYTE bIfaceCnt = 0;
  for (i = 0; i < dwNumInterfaces; pInterface++, i++) {
    DUMP_USB_INTERFACE_DESCRIPTOR(pInterface->Descriptor, i);
    BYTE bIFaceNum = pInterface->Descriptor.bInterfaceNumber;
    if (bIFaceNum >= HC_MAX_USB_IFACES) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Interface number out of bounds!\r\n")));
      return FALSE;
    }
    if ((pInterface->Descriptor.bInterfaceClass == USBSER_INTERFACE_CLASS_COMM_CTRL) &&
        (pInterface->Descriptor.bInterfaceSubClass == USBSER_INTERFACE_SUBCLASS_ACM)) {
      DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found USB CDC-ACM Control Interface at index: %d\r\n"), i));
      DevInfo[bIFaceNum].pInterface = pInterface;
      DevInfo[bIFaceNum].bDataIndex = GetDataInterface(pInterface);
      DevInfo[bIFaceNum].DevType = GetDeviceType(wProductId, bIfaceCnt);
      DevInfo[bIFaceNum].bDeviceIndex = GetPortIndex(wProductId, bIfaceCnt);
      if (DevInfo[bIFaceNum].DevType != HC_SERIAL) {
        DevInfo[bIFaceNum].DevType = HC_UNKNOWN;
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Invalid port type assigned to CDC-ACM control interface!\r\n")));
      }
      bIfaceCnt++;
    } else if ((pInterface->Descriptor.bInterfaceClass == USBSER_INTERFACE_CLASS_COMM_CTRL) &&
        (pInterface->Descriptor.bInterfaceSubClass == USBSER_INTERFACE_SUBCLASS_ECM)) {
      DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found USB CDC-ECM Control Interface at index: %d\r\n"), i));
      DevInfo[bIFaceNum].pInterface = pInterface;
      DevInfo[bIFaceNum].bDataIndex = GetDataInterface(pInterface);
      DevInfo[bIFaceNum].DevType = GetDeviceType(wProductId, bIfaceCnt);
      DevInfo[bIFaceNum].bDeviceIndex = GetPortIndex(wProductId, bIfaceCnt);
      if (DevInfo[bIFaceNum].DevType != HC_NDIS) {
        DevInfo[bIFaceNum].DevType = HC_UNKNOWN;
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Invalid port type assigned to CDC-ECM control interface!\r\n")));
      }
      bIfaceCnt++;
    } else if (pInterface->Descriptor.bInterfaceClass == USBSER_INTERFACE_CLASS_COMM_DATA) {
      // check if this data interface has 2 endpoints as expected by this driver
      // otherwise this is an alternate interface configuration
      if (pInterface->Descriptor.bNumEndpoints == 2)
      {
        USB_TRANSFER  hTransfer;
        DWORD         dwUsbErr;
    
        DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found USB Data Interface at index: %d\r\n"), i));
        DevInfo[bIFaceNum].pInterface = pInterface;
        // ensure that this interface configuration is set (interface may be 
        // alternative configuration for this interface id)
        DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: set alternate configuration %d for interface %d\r\n"), 
           pInterface->Descriptor.bAlternateSetting, pInterface->Descriptor.bInterfaceNumber ));
        hTransfer = m_pFuncs->lpSetInterface(m_hDevice, NULL, NULL, 0, pInterface->Descriptor.bInterfaceNumber, pInterface->Descriptor.bAlternateSetting);
        if (!hTransfer) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpSetInterface(...) - failed\r\n")));
          DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to create transfer to set alternate interface: %d\r\n"), GetLastError()));
          return FALSE;
        }
        if ( m_pFuncs->lpGetTransferStatus(hTransfer, NULL, &dwUsbErr) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
        }
        if (dwUsbErr) {
          DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to set alternate interface: %d\r\n"), dwUsbErr));
          return FALSE;
        }
        if ( m_pFuncs->lpCloseTransfer(hTransfer) == FALSE ) {
          DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
        }
      }
      else
      {
         DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found USB Data Interface at index: %d with %d endpoints (expected: 2)\r\n"), i, pInterface->Descriptor.bNumEndpoints));
      }
    } else if ((pInterface->Descriptor.bNumEndpoints == 2) || (pInterface->Descriptor.bNumEndpoints == 3)) {
      DevInfo[bIFaceNum].pInterface = pInterface;
      DevInfo[bIFaceNum].DevType = GetDeviceType(wProductId, bIfaceCnt);
      DevInfo[bIFaceNum].bDeviceIndex = GetPortIndex(wProductId, bIfaceCnt);
      DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found vendor specific USB Interface type %d at index: %d\r\n"), DevInfo[bIFaceNum].DevType, i));
      bIfaceCnt++;
    } else {
      DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found unsupported USB Interface at index: %d\r\n"), i));
    }
  }

  // Now activate the found devices
  if (!ActivateDevices(DevInfo, ARRAYSIZE(DevInfo), dwClientInfo)) {
    return FALSE;
  }

  // If the capability is available enable remote wakeup
  if (pDevice->lpActiveConfig->Descriptor.bmAttributes & USB_CONFIG_REMOTE_WAKEUP) {
    USB_TRANSFER  hTransfer;
    DWORD         dwUsbErr;
    DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: USB device has remote wakeup capability\r\n")));
    hTransfer = m_pFuncs->lpSetFeature(m_hDevice, NULL, NULL, USB_SEND_TO_DEVICE, USB_FEATURE_REMOTE_WAKEUP, 0);
    if (!hTransfer) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpSetFeature(RemoteWakeup) - failed\r\n")));
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to create transfer to enable Remote-Wakeup feature: %d\r\n"), GetLastError()));
      return FALSE;
    }
    if ( m_pFuncs->lpGetTransferStatus(hTransfer, NULL, &dwUsbErr) == FALSE ) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpGetTransferStatus() - failed\r\n")));
    }
    if (dwUsbErr) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to enable Remote-Wakeup feature: %d\r\n"), dwUsbErr));
      return FALSE;
    }
    if ( m_pFuncs->lpCloseTransfer(hTransfer) == FALSE ) {
      DEBUGMSG(ZONE_USB_HOSTFN_ERROR, (TEXT("HcUsb: lpCloseTransfer() - failed\r\n")));
    }
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Activate the devices corresponding to the found interfaces.
//
// Parameters:
// pDevInfo    : Array containing the info required for device activation.
// bDevInfoSize: Size of the array.
// dwClientInfo: Value saved in the registry value "ClientInfo" by call to
//               ActivateDevice().
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::ActivateDevices(PHC_DEVICE_INFO pDevInfo, BYTE bDevInfoSize, DWORD dwClientInfo) {
DWORD i;

  // First step through the found ports and create the cdc/acm and serial devices
  for (i = 0; i < bDevInfoSize; i++) {
    if (!m_fUsbAttached) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: USB Detached during interface initialization!\r\n")));
      return FALSE;
    }
    if ((pDevInfo[i].DevType == HC_SERIAL) && (pDevInfo[i].bDataIndex != (BYTE)-1) && pDevInfo[i].pInterface) {
      if ((pDevInfo[i].bDataIndex < bDevInfoSize) && pDevInfo[pDevInfo[i].bDataIndex].pInterface) {
        m_pCurrCdcCtrlIface = pDevInfo[i].pInterface;
        m_pCurrCdcDataIface = pDevInfo[pDevInfo[i].bDataIndex].pInterface;
        m_bCurrPortCaps = GetInterfaceCaps(m_pCurrCdcCtrlIface);
        m_pCurrUsbIface = m_pCurrCdcCtrlIface;
        m_bCurrInterfaceNum = m_pCurrCdcCtrlIface->Descriptor.bInterfaceNumber;
        if (!ActivatePortDevice(HC_SERIAL, dwClientInfo, pDevInfo[i].bDeviceIndex)) {
          DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to activate virtual cdc/acm device!\r\n")));
          return FALSE;
        }
      }
    } else if ((pDevInfo[i].DevType == HC_SERIAL) && pDevInfo[i].pInterface) {
      m_pCurrCdcCtrlIface = NULL;
      m_pCurrCdcDataIface = NULL;
      m_pCurrUsbIface = pDevInfo[i].pInterface;
      m_bCurrInterfaceNum = m_pCurrUsbIface->Descriptor.bInterfaceNumber;
      m_bCurrPortCaps = GetInterfaceCaps(m_pCurrUsbIface);
      if (!ActivatePortDevice(HC_SERIAL, dwClientInfo, pDevInfo[i].bDeviceIndex)) {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to activate virtual serial device!\r\n")));
        return FALSE;
      }
    }
  }

  // Now step through the found ports and create the ndis devices
  for (i = 0; i < bDevInfoSize; i++) {
    if (!m_fUsbAttached) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: USB Detached during interface initialization!\r\n")));
      return FALSE;
    }
    if ((pDevInfo[i].DevType == HC_NDIS) && pDevInfo[i].pInterface) {
      m_pCurrCdcCtrlIface = NULL;
      if (pDevInfo[i].bDataIndex != (BYTE)-1)
      {
         m_pCurrCdcDataIface = pDevInfo[pDevInfo[i].bDataIndex].pInterface;
      }
      else
      {
         m_pCurrCdcDataIface = NULL;
      }
      m_pCurrUsbIface = pDevInfo[i].pInterface;
      m_bCurrInterfaceNum = m_pCurrUsbIface->Descriptor.bInterfaceNumber;
      m_bCurrPortCaps = GetInterfaceCaps(m_pCurrUsbIface);
      if (!ActivatePortDevice(HC_NDIS, dwClientInfo, pDevInfo[i].bDeviceIndex)) {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to activate virtual ndis device!\r\n")));
        return FALSE;
      }
      // Activate the corresponding command port
      m_pCurrUsbIface = NULL;
      m_bCurrPortCaps = 0;
      if (!ActivatePortDevice(HC_CMD, dwClientInfo, pDevInfo[i].bDeviceIndex)) {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Failed to activate virtual port device!\r\n")));
        return FALSE;
      }
    }
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Activate the corresponding port device.
//
// Parameters:
// DevType     : The type of the corresponding port (serial, cmd or ndis).
// dwClientInfo: Value saved in the registry value "ClientInfo" by call to
//               ActivateDevice().
// dwIndex     : The desired device index.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::ActivatePortDevice(HC_DEVICE_TYPE DevType, DWORD dwClientInfo, DWORD dwIndex) {
HANDLE   hStreamDevice;
HKEY     hKey;
LPCTSTR  strPrefix;
DWORD    dwRegDataSize;
LPCTSTR  szDevKey;

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: +CUsbHandling::ActivatePortDevice()\r\n")));

  // Get the correct device prefix
  switch (DevType) {
    case HC_SERIAL:
      strPrefix = SERIAL_PORT_PREFIX;
      dwRegDataSize = sizeof(SERIAL_PORT_PREFIX);
      break;
    case HC_CMD:
      strPrefix = CMD_PORT_PREFIX;
      dwRegDataSize = sizeof(CMD_PORT_PREFIX);
      break;
    case HC_NDIS:
      strPrefix = NDIS_PORT_PREFIX;
      dwRegDataSize = sizeof(NDIS_PORT_PREFIX);
      break;
    default:
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): Unknown device type: %d!\r\n"), DevType));
      DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(FALSE)\r\n")));
      return FALSE;
  }

  // We open the clients registry section to be able to handle our different device prefixes.
  szDevKey = HCUSBDRV_CLIENT_REGKEY;
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDevKey, 0, 0, &hKey) != ERROR_SUCCESS) {
    szDevKey = HCUSBDRV_CLIENT_REGKEY_COMPATIBLITY;
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDevKey, 0, 0, &hKey) != ERROR_SUCCESS) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): Failed to opend client registry key!\r\n")));
      DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(FALSE)\r\n")));
      return FALSE;
    }
  }

  if (FAILED(RegSetValueEx(hKey, TEXT("Prefix"), 0, REG_SZ, (BYTE*)strPrefix, dwRegDataSize))) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): Failed to write device prefix value to registry key!\r\n")));
    RegCloseKey(hKey);
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(FALSE)\r\n")));
    return FALSE;
  }

  // Reset init wait event
  ResetEvent(m_hIfaceReadyEvent);

  // Check if there is already an activated port for this usb interface
  CVirtualPort * pVirtPort = CUsbDriver::GetActivatedPort(DevType, dwIndex); //m_bCurrInterfaceNum);

  if (pVirtPort != NULL) {
    // We're still usb-attached (new device is attached before old is detached)
    // instead of deactivated and re-activating the device we just re-initialize the usb handling
    DEBUGMSG(ZONE_PORT, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): - already activated\r\n")));

    // ensure that the port is closed before the device is re-initialized
    pVirtPort->EnsurePortClosed();
    hStreamDevice = pVirtPort->GetStreamHandle();

    pVirtPort->GetBulkOutPipe()->Deinitialize();
    pVirtPort->GetBulkInPipe()->Deinitialize();
    pVirtPort->GetIrqInPipe()->Deinitialize();

    pVirtPort->SetUsbHandling(this);
  } else {
    if (dwIndex != (DWORD)-1) {
      dwRegDataSize = sizeof(DWORD);
      RegSetValueEx(hKey, TEXT("Index"), 0, REG_DWORD, (BYTE*)&dwIndex, dwRegDataSize);
    } else {
      RegDeleteValue(hKey, TEXT("Index"));
    }

    m_dwCurrVirtualPortIndex = dwIndex;

    // Activate the corresponding device
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): activate system device \"%s%d:\"\r\n"), strPrefix, dwIndex));
    hStreamDevice = ActivateDevice(szDevKey, dwClientInfo);
    if (!hStreamDevice && (dwIndex != (DWORD)-1)) {
      DEBUGMSG(ZONE_USB, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): Failed to activate device with given index %d (error %d), retrying with automatic index\r\n"), dwIndex, GetLastError()));
      RegDeleteValue(hKey, TEXT("Index"));
      hStreamDevice = ActivateDevice(szDevKey, dwClientInfo);
    }
    if (!hStreamDevice) {
      DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): ActivateDevice() error: %d\r\n"), GetLastError()));
      RegCloseKey(hKey);
      DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(FALSE)\r\n")));
      return FALSE;
    }
    if (m_pCurrVirtPort != NULL) {
      m_pCurrVirtPort->SetStreamHandle(hStreamDevice);
    }
  }

  RegCloseKey(hKey);

  // Add the stream device handle to our internal list
  if (!AddStreamDevice(hStreamDevice)) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): Failed to add the new stream device to our internal list\r\n")));
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(FALSE)\r\n")));
    return FALSE;
  }

  // And now wait until the interface has been initialized by the activated device
  UNSYNCBLOCK(m_csLock);
  DEBUGMSG(ZONE_USB & ZONE_INFO, (TEXT("HcUsb:  CUsbHandling::ActivatePortDevice(): Waiting for port device to be created ...\r\n")));
  if (WaitForSingleObject(m_hIfaceReadyEvent, INIT_INTERFACE_TIMEOUT) != WAIT_OBJECT_0) {
    DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: Timeout while waiting for new port device to call us back!\r\n")));
    DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(FALSE)\r\n")));
    return FALSE;
  }

  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -CUsbHandling::ActivatePortDevice(TRUE)\r\n")));
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Add a new stream device to our internal list.
//
// Parameters:
// hStreamDevice: Handle of the new stream device to be added.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::AddStreamDevice(HANDLE hStreamDevice) {
DWORD i;

  if (hStreamDevice) {
    for (i = 0; i < MAX_VIRTUAL_PORTS; i++) {
      if (!m_hStreamDevices[i]) {
        m_hStreamDevices[i] = hStreamDevice;
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Deactivate all devices from our internal list.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates success. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::DeactivateDevices(void) {
DWORD  i;
BOOL   fRet = TRUE;

  for (i = 0; i < MAX_VIRTUAL_PORTS; i++) {
    if (m_hStreamDevices[i]) {
      if (!DeactivateDevice(m_hStreamDevices[i])) {
        DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb: CUsbHandling::DeactivateDevices() failed on port %i \r\n"), i));
        fRet = FALSE;
      }
      m_hStreamDevices[i] = NULL;
    }
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Called in "USBDetach()" before the driver instance is destroyed by a call
// to "DeactivateDevice()" to have a chance to tidy up possible open port
// instances. The function blocks until all open port instances have been
// closed properly or the virtual port handler is destroyed.
//
// Parameters:
// None.
//
// Return:
// TRUE indicates that port is open. FALSE indicates failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::EnsurePortsClosed(void) {
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: +EnsurePortsClosed()\r\n")));
  BOOL fRet = (WaitForSingleObject(m_hPortsClosedEvent, INFINITE) == WAIT_OBJECT_0) ? TRUE : FALSE;
  Sleep(100);
  DEBUGMSG(ZONE_USB, (TEXT("HcUsb: -EnsurePortsClosed(%d)\r\n"), fRet));
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the type of a given device.
//
// Parameters:
// wProductId: The USB id of our device.
// bIfaceNum : The number of the interface.
//
// Return:
// The device type.
//
//////////////////////////////////////////////////////////////////////////////
HC_DEVICE_TYPE CUsbHandling::GetDeviceType(WORD wProductId, BYTE bIfaceNum) {
HKEY   hKey;
DWORD  i;

  // First try to find the registry key for device assignment
  if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, 0, &hKey)) == ERROR_SUCCESS) {
    TCHAR strBuf[1024];
    DWORD  dwRegDataSize = sizeof(strBuf);
    strBuf[0] = 0;
    if ((RegQueryValueEx(hKey, USB_PORT_ASSIGNMENT_NAME, 0, NULL, (LPBYTE)(strBuf), &dwRegDataSize) == ERROR_SUCCESS)) {
      HC_DEVICE_TYPE ret = HC_UNKNOWN;
      dwRegDataSize = (dwRegDataSize / sizeof(TCHAR)) - 1;
      TCHAR *pBuf = strBuf;
      BYTE bCnt = 0;
      while ((DWORD)(pBuf - strBuf) < dwRegDataSize) {
        if (bCnt == bIfaceNum) {
          if (!_tcsicmp(pBuf, TEXT("Serial"))) {
            DEBUGMSG(ZONE_USB, (TEXT("HcUsb: USB Interface %d assigned to Serial via registry\r\n"), bIfaceNum));
            ret = HC_SERIAL;
          } else if (!_tcsicmp(pBuf, TEXT("Ndis"))) {
            DEBUGMSG(ZONE_USB, (TEXT("HcUsb: USB Interface %d assigned to Ndis via registry\r\n"), bIfaceNum));
            ret = HC_NDIS;
          } else {
            DEBUGMSG(ZONE_USB, (TEXT("HcUsb: USB Interface %d disabled via registry\r\n"), bIfaceNum));
          }
          RegCloseKey(hKey);
          return ret;
        }
        pBuf += _tcslen(pBuf) + 1;
        bCnt++;
      }
    }
    RegCloseKey(hKey);
  }

  // Try to find a matching entry in our device table
  for (i = 0; i < ARRAYSIZE(ProductIfaces); i++) {
    if (ProductIfaces[i].wProductId == wProductId) {
      if (bIfaceNum < ProductIfaces[i].wNumOfPorts) {
        return (ProductIfaces[i].Ports[bIfaceNum]);
      }
      break;
    }
  }

  // No matching entry found -> Try to find the default entry in our device table
  for (i = 0; i < ARRAYSIZE(ProductIfaces); i++) {
    if (ProductIfaces[i].wProductId == 0) {
      if (bIfaceNum < ProductIfaces[i].wNumOfPorts) {
        return (ProductIfaces[i].Ports[bIfaceNum]);
      }
      break;
    }
  }

  return HC_UNKNOWN;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the desired index of a given interface.
//
// Parameters:
// wProductId: The USB id of our device.
// bIfaceNum : The number of the interface.
//
// Return:
// The desired device index or -1 in case of an error.
//
//////////////////////////////////////////////////////////////////////////////
BYTE CUsbHandling::GetPortIndex(WORD wProductId, BYTE bIfaceNum) {
HKEY   hKey;
DWORD  i;

  // First try to find the registry key for device assignment
  if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, HCUSBDRV_CLIENT_REGKEY, 0, 0, &hKey)) == ERROR_SUCCESS) {
    TCHAR strBuf[1024];
    DWORD  dwRegDataSize = sizeof(strBuf);
    strBuf[0] = 0;
    if ((RegQueryValueEx(hKey, USB_PORT_INDEXES_NAME, 0, NULL, (LPBYTE)(strBuf), &dwRegDataSize) == ERROR_SUCCESS)) {
      dwRegDataSize = (dwRegDataSize / sizeof(TCHAR)) - 1;
      TCHAR *pBuf = strBuf;
      BYTE bCnt = 0;
      while ((DWORD)(pBuf - strBuf) < dwRegDataSize) {
        if (bCnt == bIfaceNum) {
          BYTE bRet = (BYTE)_ttoi(pBuf);
          DEBUGMSG(ZONE_USB, (TEXT("HcUsb: USB Interface %d has desired device index %d via registry\r\n"), bIfaceNum, bRet));
          RegCloseKey(hKey);
          return bRet;
        }
        pBuf += _tcslen(pBuf) + 1;
        bCnt++;
      }
    }
    RegCloseKey(hKey);
  }

  // Try to find a matching entry in our device table
  for (i = 0; i < ARRAYSIZE(ProductIfaces); i++) {
    if (ProductIfaces[i].wProductId == wProductId) {
      if (bIfaceNum < ProductIfaces[i].wNumOfPorts) {
        return (ProductIfaces[i].Indexes[bIfaceNum]);
      }
      break;
    }
  }

  // No matching entry found -> Try to find the default entry in our device table
  for (i = 0; i < ARRAYSIZE(ProductIfaces); i++) {
    if (ProductIfaces[i].wProductId == 0) {
      if (bIfaceNum < ProductIfaces[i].wNumOfPorts) {
        return (ProductIfaces[i].Indexes[bIfaceNum]);
      }
      break;
    }
  }

  return -1;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the capabilities of a given interface descriptor.
//
// Parameters:
// pInterface: Pointer to the interface descriptor.
//
// Return:
// The interface capabilities.
//
//////////////////////////////////////////////////////////////////////////////
BYTE CUsbHandling::GetInterfaceCaps(LPCUSB_INTERFACE pInterface) {
#ifdef HCUSBDRV_CE6
  if (pInterface->lpvExtended && pInterface->dwExtendedSize) {
#else
  if (pInterface->lpvExtended) {
#endif
    // If available parse the functional descriptors of the control interface for virtual port capabilities
    PBYTE pExtended = (PBYTE)pInterface->lpvExtended;
#ifdef HCUSBDRV_CE6
    while (pExtended < ((PBYTE)pInterface->lpvExtended + pInterface->dwExtendedSize)) {
#else
    while (pExtended[0]) {
#endif
      if (pExtended[0] > 3) {
        if ((pExtended[1] == CS_INTERFACE) && (pExtended[2] == DESC_SUBTYPE_ABSTRACT_CTRL_MGT)) {
          // We found what we were looking for
          DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found virtual USB port capabilities: 0x%02x\r\n"), pExtended[3]));
          return pExtended[3];
        }
      }
      pExtended += pExtended[0];
    }
  }
  return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the index of the data interface for a Cdc/Acm port.
//
// Parameters:
// pInterface: Pointer to the interface descriptor.
//
// Return:
// The index of the data interface or -1 in case of an error.
//
//////////////////////////////////////////////////////////////////////////////
BYTE CUsbHandling::GetDataInterface(LPCUSB_INTERFACE pInterface) {
#ifdef HCUSBDRV_CE6
  if (pInterface->lpvExtended && pInterface->dwExtendedSize) {
#else
  if (pInterface->lpvExtended) {
#endif
    // If available parse the functional descriptors of the control interface for virtual port capabilities
    PBYTE pExtended = (PBYTE)pInterface->lpvExtended;
#ifdef HCUSBDRV_CE6
    while (pExtended < ((PBYTE)pInterface->lpvExtended + pInterface->dwExtendedSize)) {
#else
    while (pExtended[0]) {
#endif
      if (pExtended[0] > 4) {
        if ((pExtended[1] == CS_INTERFACE) && (pExtended[2] == DESC_SUBTYPE_CALL_MGT)) {
          // We found what we were looking for
          DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found data interface index %d for Cdc/Acm USB port\r\n"), pExtended[4]));
          return pExtended[4];
        }
        else if ((pExtended[1] == CS_INTERFACE) && (pExtended[2] == DESC_SUBTYPE_UNION)) {
          // We found what we were looking for (part II)
          DEBUGMSG(ZONE_USB_PARSE, (TEXT("HcUsb: Found slave interface index %d for Cdc/Ecm USB port\r\n"), pExtended[4]));
          return pExtended[4];
        }
      }
      pExtended += pExtended[0];
    }
  }
  return -1;
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
DWORD CUsbHandling::DispatchDeviceRequest( PUSB_DEVICE_REQUEST pDevRequest ) {
  if ( pDevRequest == NULL ) {
    return -1;
  }
  SYNCBLOCK(m_csDeviceRequestLock);
  // get interface from request and find associated port for it
  DEBUGMSG(ZONE_USB & ZONE_INFO, (TEXT("HcUsb:   DispatchDeviceRequest() - dispatch request to IF %d\r\n"), pDevRequest->wIndex));
  for ( DWORD i = 0; i < MAX_VIRTUAL_PORTS; i++ ) {
    CVirtualPort *pVirtualPort = m_pVirtualPorts[i];

    if ((pVirtualPort != NULL) &&
        (pVirtualPort->GetUsbInterfaceNumber() == pDevRequest->wIndex)) {
      // dispatch request to port
      DWORD dwRet = pVirtualPort->HandleDeviceRequest( pDevRequest );
      DEBUGMSG(ZONE_USB & ZONE_INFO, (TEXT("HcUsb:  -DispatchDeviceRequest(%d) - request handled\r\n"), dwRet));
      return dwRet;
    }
  }
  DEBUGMSG(ZONE_USB | ZONE_ERROR, (TEXT("HcUsb:   DispatchDeviceRequest() - no port found for interface\r\n")));
  DEBUGMSG(ZONE_USB & ZONE_INFO,  (TEXT("HcUsb:  -DispatchDeviceRequest(-1)\r\n")));
  return -1;
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::AddVirtualport( CVirtualPort *pVirtualPort ) {
  for ( DWORD i = 0; i < MAX_VIRTUAL_PORTS; i++ ) {
    if ( m_pVirtualPorts[i] == NULL ) {
      m_pVirtualPorts[i] = pVirtualPort;
      DEBUGMSG(ZONE_PORT|ZONE_USB, (TEXT("HcUsb:  CUsbHandling::AddVirtualport(): port=%p cnt=%d\r\n"), pVirtualPort, i));
      return TRUE;
    }
  }

  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
BOOL CUsbHandling::RemoveVirtualport( CVirtualPort *pVirtualPort ) {
  for ( DWORD i = 0; i < MAX_VIRTUAL_PORTS; i++ ) {
    if ( m_pVirtualPorts[i] == pVirtualPort ) {
      m_pVirtualPorts[i] = NULL;
      return TRUE;
    }
  }

  return FALSE;
}

