/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/ComManager.cpp $
 * $Revision: /main/ms-d/66 $
 *     $Date: 2014-05-08 10:31:48 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/ComManager.cpp$ 
 * 
 * /main/ms-d/66   2014-05-08 10:31:48   pmackent 
 * Move AT^SAIC to RIL initialization (IPIS100096235)
 *
 * /main/ms-d/65   2013-10-24 13:41:18   pmackent
 * Preparation to support Windows Embedded Compact 2013
 *
 * /main/ms-d/64   2013-06-27 16:00:05   pmackent
 * Adaptions for EHS5 Rel1->Rel2 update
 *
 * /main/ms-d/63   2013-03-13 15:04:26   pmackent
 * Additional debug traces for signal critical error.
 *
 * /main/ms-d/62   2013-03-05 11:33:29   pmackent
 * Handle ^SYSSTART URC for EHS5 Rel2
 *
 * /main/ms-d/61   2013-03-04 13:19:32   pmackent
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/60   2013-02-27 15:18:10   pmackent
 * Introduce provider spezific PRL update
 *
 * /main/ms-d/59   2012-10-31 12:09:43   pmackent
 * Moce initialization of temperture warning to early RIL initialization
 *
 * /main/ms-d/58   2012-10-24 10:55:32   pmackent
 * Allow different audio configuration for CDMA and GSM
 *
 * /main/ms-d/57   2012-07-06 11:29:38   pmackent
 * Allow PPP traces on CSD/CDMA
 *
 * /main/ms-d/56   2012-02-24 14:20:48   ctna0256
 * Extra initialization for data port on EHS5
 *
 * /main/ms-d/55   2012-01-13 10:09:53   ctna0256
 * Implement audio workaround with SNFO/SNFI set to 0 (IPIS100076299)
 *
 * /main/ms-d/54   2011-12-22 10:45:25   ctna0256
 * Optional separation of audio commands (IPIS100076299)
 *
 * /main/ms-d/53   2011-09-26 13:34:17   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/52   2011-06-06 13:54:36   ctna0256
 * New registry key for URC indication methods (IPIS100073124)
 *
 * /main/ms-d/51   2011-03-09 14:24:36   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/50   2010-12-13 16:06:55   ctna0256
 * Allow automatic restart on configuration changes
 *
 * /main/ms-d/49   2010-10-12 11:12:03   ctna0256
 * Advanced configuradion for PH8
 *
 * /main/ms-d/48   2010-08-24 10:37:54   ctna0256
 * Moved reading of port assignments from registry
 *
 * /main/ms-d/47   2010-07-02 15:58:36   ctna0256
 * Introduce SAIC-settings and modified SNFO for PH8
 *
 * /main/ms-d/46   2010-05-18 16:33:15   ctna0256
 * Additional trace for failed external write
 *
 * /main/ms-d/45   2010-05-07 10:18:23   ctna0256
 * New PPP trace feature needed to find connection problems (IPIS100064425)
 *
 * /main/ms-d/44   2010-02-17 14:26:58   ctna0256
 * GetRegistrySZ() might return TRUE, even on error (IPIS100062294)
 *
 * /main/ms-d/43   2010-02-03 10:50:44   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/42   2010-01-20 14:04:46   ctna0256
 * Configure ring tones with registry
 *
 * /main/ms-d/41   2010-01-20 12:03:29   ctna0256
 * Send double audio mode change after every use of SNFI outside of voice calls (IPIS100061295)
 *
 * /main/ms-d/40   2009-05-05 10:21:50   ctna0256
 * Deactivate AT^SNFO and AT^SNFI with registry key.
 *
 * /main/ms-d/39   2009-02-02 15:23:24   ctna0450
 * Fixed restauration of external WaitCommMask after port reopen
 *
 * /main/ms-d/38   2009-01-16 12:44:26   ctna0450
 * New mechanism to prevent PPP data from slip into the AT response buffer
 *
 * /main/ms-d/37   2009-01-14 10:50:00   ctna0450
 * Removed special handling for consecutive WaitCommEvent() calls because mux ports are properly instancable now
 *
 * /main/ms-d/36   2008-12-19 15:58:28   ctna0256
 * Set audio parameter to zero as default
 *
 * /main/ms-d/35   2008-08-22 10:23:06   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/34   2008-08-21 09:34:09   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/33   2008-07-22 15:53:15   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/32   2008-07-22 15:12:29   mp49210
 * Removed unnecessary declaration
 *
 * /main/ms-d/31   2008-05-30 13:30:35   ht37010
 * bugfix for IPIS 47799:
 * synchronize WaitOnMask() calls from CheckCmdModeThread and the thread that uses the external data port (i.e. PPP)
 *
 * /main/ms-d/30   2008-04-29 13:39:51   mp49210
 * Removed Lint warning
 *
 * /main/ms-d/29   2008-04-16 16:24:07   mp49210
 * Debug zones for RHA traces
 *
 * /main/ms-d/28   2008-04-03 14:41:29   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/27   2007-12-11 11:01:51   kr36712
 * GPS Powermanagement
 *
 * /main/ms-d/26   2007-11-28 11:31:16   kr36712
 * error correction OpenBasePort()
 *
 *
 * /main/ms-d/25   2007-11-27 17:52:50   kr36712
 * CloseAllPortDevices() + CloseBasePort for use with MUX
 *
 * /main/ms-d/24   2007-11-26 17:05:42   kr36712
 * integration of GPS support
 * use of the mux protocol
 *
 * /main/ms-d/23   2007-11-02 18:34:48   ht37010
 * fix for EPIC 21310: corrected handling of ForcePowerOff during TurnOnModule
 *
 * /main/ms-d/22   2007-11-02 15:50:10   ht37010
 * fix for EPIC21309: passing parameter to avoid waiting for reopen if module is already powered off
 *
 * /main/ms-d/21   2007-09-21 16:08:07   mp49210
 * Optional usage of multiplexer for HC25 Rel2
 *
 * /main/ms-d/20   2007-07-18 17:09:10   ht37010
 * IPIS100039395: corrected handling of closing data connection during a suspend-resume-cycle
 *
 * /main/ms-d/19   2007-05-30 17:33:08   ht37010
 * adjusted timeouts during reopening
 *
 * /main/ms-d/18   2007-04-27 08:33:48   mp49210
 * Removed Lint warnings
 *
 * /main/ms-d/17   2007-04-26 14:24:39   mp49210
 * IPIS100035548: new RIL option: disable audio settings
 *
 * /main/ms-d/16   2007-04-11 15:49:15   ht37010
 * Added port reopen preparation before suspend as workaround for USB reset problem
 *
 * /main/ms-d/15   2007-02-20 15:39:16   wm36264
 * Reopen only previously opened ports
 *
 * /main/ms-d/14   2007-02-15 12:09:14   wm36264
 * Added port reopen after resume as workaround for USB reset problem
 *
 * /main/ms-d/13   2007-02-09 15:15:39   mp49210
 * Lint compatibility
 *
 * /main/ms-d/12   2007-01-19 10:06:53   mp49210
 * New audio configuration registry structure
 *
 * /main/ms-d/11   2007-01-03 17:04:18   mp49210
 * Replace +CIEV: sounder with +CIEV: audio
 *
 * /main/ms-d/10   2006-12-21 18:26:53   mp49210
 * Changed handling of call states
 *
 * /main/ms-d/9   2006-11-22 13:01:44   wm36264
 * Removed unused stuff
 *
 * /main/ms-d/8   2006-11-21 09:42:08   mp49210
 * Registry key for user defined init command
 *
 * /main/ms-d/7   2006-11-14 14:40:00   wm36264
 * Disposed unused CRilEventLog
 *
 * /main/ms-d/6   2006-11-02 09:31:41   mp49210
 * Minor changes for AT+CINF, AT^SNFS
 *
 * /main/ms-d/5   2006-11-01 15:02:05   wm36264
 * Send the final init marker seperately as noop command
 *
 * /main/ms-d/4   2006-11-01 10:29:40   mp49210
 * New AT+CFUN handling in Quinn
 *
 * /main/ms-d/3   2006-10-30 12:30:45   wm36264
 * Fixed trace output
 *
 * /main/ms-d/2   2006-10-30 10:26:37   wm36264
 * Removed unused stuff
 *
 * /main/ms-d/1   2006-10-25 16:53:07   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"
#include "devload.h"

//////////////////////////////////////////////////////////////////////////////
// Include the arrays with the port initialization commands
#include "ModuleInitCmds.cpp"
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Init static variables
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// This static meber variable defines the names and capabilities of the
// available module ports.
// Modify this array together with the corresponding define "NUMBER_OF_PORTS"
// to add or remove ports and/or modify the capabilities of existing ports.
//////////////////////////////////////////////////////////////////////////////
const CComManager::PortInfo CComManager::m_PortInfo[NUMBER_OF_PORTS] = {
// ----------------------------------------------------------------------------------------------------------------
//   Dbg-Name                Reg-Name              Internal-Reg-Name             Capabilities
// ----------------------------------------------------------------------------------------------------------------
  {  TEXT("data port"),      TEXT("DataPort"),     TEXT("DataPortInternal"),     PORT_CAPS_CMD | PORT_CAPS_DATA   },
  {  TEXT("urc port"),       NULL,                 TEXT("ComPort"),              PORT_CAPS_CMD | PORT_CAPS_URC    },
// ----------------------------------------------------------------------------------------------------------------
};

//////////////////////////////////////////////////////////////////////////////
// Currently the one and only instance of the CComManager
//////////////////////////////////////////////////////////////////////////////

CComManager g_ComManager;

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

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CComManager::CComManager() {
COM_PORT_TYPE i;

  InitializeCriticalSection(&(m_RilHandleLock.csLock));
  m_RilHandleLock.dwCnt = 0;
  m_hBasePort = INVALID_HANDLE_VALUE;
  m_pRilHandle= NULL;
  m_hReopenEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
#ifdef RIL_ALLOW_TRACE_PPP_DATA
  m_fTraceExtData = FALSE;
  m_fCSDConnection = FALSE;
  m_dwTraceExtDataCounterMax = DEFAULT_MAX_TRACE_DATA_COUNTER;
  m_dwTraceExtDataCounter = 0;
#endif
  for (i = 0; i < NUMBER_OF_PORTS; i++)
    m_pComDevice[i] = NULL;

  ReadRegistrySettings();
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CComManager::~CComManager() {
COM_PORT_TYPE i;

  // Close all remaining ports devices
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    (void)DestroyPortDevice(m_pComDevice[i]);
    DeleteCriticalSection(&(m_PortLock[i].csLock));
  }
  Deinit();
  CloseHandle(m_hReopenEvent);
  DeleteCriticalSection(&(m_RilHandleLock.csLock));
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize the class with required information.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::Init(CRilHandle* pRilHandle) {
  WAITNOUSAGE(m_RilHandleLock);
  m_pRilHandle= pRilHandle;

  ReadRegistrySettings();
}

//////////////////////////////////////////////////////////////////////////////
//
// Tell the class that the information passed to Init() is no longer valid.
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::Deinit(void) {
  SetEvent(m_hReopenEvent);
  WAITNOUSAGE(m_RilHandleLock);
  m_pRilHandle= NULL;
}

//////////////////////////////////////////////////////////////////////////////
//
// Reads COM port settings from the registry during initialization.
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::ReadRegistrySettings(void) {
COM_PORT_TYPE i;

  // Load the port names from the registry
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    InitializeCriticalSection(&(m_PortLock[i].csLock));
    m_PortLock[i].dwCnt = 0;
    m_PortUsage[i] = USAGE_INTERNAL;
    m_dwIoCount[i] = 0;
    m_szPortNameInternal[i][0] = 0;
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, m_PortInfo[i].strRegNameInternal, m_szPortNameInternal[i], MAX_PORT_NAME_LEN)) {
      m_szPortNameInternal[i][0] = 0;
    }
    m_szPortName[i][0] = 0;
    if (IsDataPort(i) && m_PortInfo[i].strRegName) {
      if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, m_PortInfo[i].strRegName, m_szPortName[i], MAX_PORT_NAME_LEN)) {
        m_szPortName[i][0] = 0;
      }
    }
  }

  // Load information of possibly available GPS port from registry
  m_szInternalGpsPortName[0] = 0;
  if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsPortInternal"), m_szInternalGpsPortName, MAX_PORT_NAME_LEN)) {
    m_szInternalGpsPortName[0] = 0;
  }

  // Find the port assignments
  m_DataPort = INVALID_PORT;
  m_UrcPort = INVALID_PORT;
  for (i = 0; i < NUMBER_OF_PORTS; i++) {
    if (m_PortInfo[i].dwCaps & PORT_CAPS_DATA) {
      if (m_DataPort == INVALID_PORT) {
        m_DataPort = i;
      }
    }
    if (m_PortInfo[i].dwCaps & PORT_CAPS_URC) {
      if (m_UrcPort == INVALID_PORT) {
        m_UrcPort = i;
      }
    }
  }
}


//////////////////////////////////////////////////////////////////////////////
//
// Get the port name used by FileOpen() for application usage.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  Pointer to the port's name or NULL if the port doesn't exist.
//
//////////////////////////////////////////////////////////////////////////////
TCHAR* CComManager::GetPortName(COM_PORT_TYPE PortType) {
TCHAR* pRet = NULL;

  if (_tcslen(m_szPortName[PortType])) {
    pRet = m_szPortName[PortType];
  }
  return pRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the port name used by FileOpen() for internal usage.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  Pointer to the port's name or NULL if the port doesn't exist.
//
//////////////////////////////////////////////////////////////////////////////
TCHAR* CComManager::GetInternalPortName(COM_PORT_TYPE PortType) {
TCHAR* pRet = NULL;

  if (IsValidPort(PortType)) {
    if (_tcslen(m_szPortNameInternal[PortType])) {
      pRet = m_szPortNameInternal[PortType];
    }
  }
  return pRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Try to open the given registry section and find the port device
// corresponding to the given "Index" value. If there is no port device
// created yet it will be created by the function.
//
// Params:
//  szRegKey: The registry section of the given port passed to the open
//            function.
//
// Return:
//  Pointer to the port device corresponding to the given registry key or
//  NULL in case of an Error.
//
//////////////////////////////////////////////////////////////////////////////
CComHandle* CComManager::CreatePortDevice(LPCWSTR szRegKey) {
CComHandle*  pRet;
HKEY         hKey;
DWORD        dwValueType;
DWORD        dwDataSize;
DWORD        dwIndex;

  pRet = NULL;
  if (szRegKey) {
    hKey = OpenDeviceKey(szRegKey);
    if (hKey) {
      // Fetch the index
      dwDataSize = sizeof(DWORD);
      if (SUCCEEDED(RegQueryValueEx(hKey, L"Index", NULL, &dwValueType, (LPBYTE)&(dwIndex), &dwDataSize))) {
        if (dwValueType == REG_DWORD) {
          if (dwDataSize == sizeof(DWORD)) {
            // Here we're sure that we have a valid port index -> get the corresponding port device
            COM_PORT_TYPE  i;
            DWORD          d;
            for (i = 0, d = 0; i < NUMBER_OF_PORTS; i++) {
              if (IsDataPort(i)) {
                d++;
                if (d >= dwIndex) {
                  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Creating port device for \"%s\" (%s)\r\n"), GetPortName(i), GetDebugName(i)));
                  pRet = CreatePortDevice(i);
                  break;
                }
              }
            }
          }
        }
      }
      (void)RegCloseKey(hKey);
    }
  }
  if (!pRet) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No port device found for registry section \"%s\", data port not available!\r\n"), szRegKey));
  }
  return pRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Find the port device corresponding to the given port type. If there is no
// port device created yet it will be created by the function.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  Pointer to the port device corresponding to the given port type NULL in
//  case of an Error.
//
//////////////////////////////////////////////////////////////////////////////
CComHandle* CComManager::CreatePortDevice(COM_PORT_TYPE PortType) {
  WAITNOUSAGE(m_PortLock[PortType]);
  if (!m_pComDevice[PortType]) {
    m_pComDevice[PortType] = new CComHandle(PortType);
    if (m_pComDevice[PortType]) {
      if (!m_pComDevice[PortType]->Init()) {
        delete m_pComDevice[PortType];
        m_pComDevice[PortType] = NULL;
      }
    }
    if (m_pComDevice[PortType]) {
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Port device for \"%s\" has been successfully created\r\n"), GetDebugName(PortType)));
    } else {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Failed to create port device for \"%s\"!\r\n"), GetDebugName(PortType)));
    }
  }
  (void)LockPortDevice(PortType);
  return m_pComDevice[PortType];
}

//////////////////////////////////////////////////////////////////////////////
//
// Close the port device corresponding to the given port type. The device will
// only be destroyed if it is no data port because data ports might still be
// in usage by external applications.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  TRUE if the port device was destroyed, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::DestroyPortDevice(COM_PORT_TYPE PortType) {
  WAITNOUSAGE(m_PortLock[PortType]);
  if (m_pComDevice[PortType]) {
    if (!IsDataPort(PortType)) {
      (void)LockPortDevice(PortType);
      delete m_pComDevice[PortType];
      m_pComDevice[PortType] = NULL;
    } else {
      (void)UnlockPortDevice(PortType);
    }
  }
  return m_pComDevice[PortType] == NULL;
}

//////////////////////////////////////////////////////////////////////////////
//
// Destroy the port device corresponding to the given pointer.
//
// Params:
//  pPortDevice: Pointer to the port device we have to destroy.
//
// Return:
//  TRUE if the port device was destroyed, FALSE if there was no port device
//  corresponding to the given pointer found.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::DestroyPortDevice(const CComHandle* pPortDevice) {
COM_PORT_TYPE i;

  if (pPortDevice) {
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      WAITNOUSAGE(m_PortLock[i]);
      if (m_pComDevice[i] == pPortDevice) {
        (void)LockPortDevice(i);
        delete m_pComDevice[i];
        m_pComDevice[i] = NULL;
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Open a port device for usage by RIL driver.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::OpenPortDevice(COM_PORT_TYPE PortType) {
  if (IsValidPort(PortType)) {
    CNTUSAGE(&(m_PortLock[PortType]));
    if (m_pComDevice[PortType]) {
      if (m_pComDevice[PortType]->OpenDownstreamPort(m_pRilHandle)) {
        // Lock the Get() function of the response queue
        (void)g_RspQ.SetReadLock(TRUE);
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Close a port device for further usage by RIL driver.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::ClosePortDevice(COM_PORT_TYPE PortType) {
  if (IsValidPort(PortType)) {
    CNTUSAGE(&(m_PortLock[PortType]));
    if (m_pComDevice[PortType]) {
      // Unlock the Get() function of the response queue
      (void)g_RspQ.SetReadLock(FALSE);
      if (m_pComDevice[PortType]->CloseDownstreamPort(FALSE)) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Close all port devices for further usage by RIL driver.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::CloseAllPortDevices(void) {
COM_PORT_TYPE  i;
BOOL           fRet = FALSE;

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    fRet = TRUE;
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      (void)ClosePortDevice(i);
    }
    g_PowerManager.DisableCmdRequeueWait();
  }

  (void)CloseBasePort();

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Open and init the base port in case of mux driver usage, open and init
// the 1st serial module port otherwise.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::OpenBasePort(void) {
DWORD  dwBaudRate;
DWORD  dwLen = MAX_PATH;
DWORD  dwActiveLen = MAX_PATH;
TCHAR  szKey[MAX_PATH];
TCHAR  szDummy[MAX_PATH];

  CNTUSAGE(&m_RilHandleLock);

  if (m_hBasePort == INVALID_HANDLE_VALUE) {
    // Try to read the mux base port from the registry
    if (!GetDeviceKeys(TEXT("MPX1:"), szDummy, &dwActiveLen, szKey, &dwLen)) {
      if (dwLen) {
        memset(szDummy, 0, sizeof(szDummy));
        if (GetRegistrySZ(HKEY_LOCAL_MACHINE, szKey, TEXT("ParentPort"), szDummy, MAX_PATH)) {
          if ( g_PowerManager.IsPowerOffForced() == TRUE ) {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Error - already forced power off\r\n")));
            return FALSE;
          }
          m_hBasePort = CreateFile(szDummy, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
        }
      }
    } else {
      // mux not installed
      if ( g_PowerManager.IsPowerOffForced() == TRUE ) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Error - already forced power off\r\n")));
        return FALSE;
      }
      m_hBasePort = CreateFile(m_szPortNameInternal[DATA_PORT], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    }
    if (m_hBasePort != INVALID_HANDLE_VALUE) {
      DCB           dcb;
      COMMTIMEOUTS  ct;

      // Fetch the required baud rate
      if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("BaudRate"), &(dwBaudRate))) {
        dwBaudRate = CBR_115200;
      }

      (void)ClearCommError(m_hBasePort, &dwLen, NULL);
      (void)GetCommState(m_hBasePort, &dcb);
      dcb.BaudRate                    = dwBaudRate;
      dcb.fBinary                     = TRUE;
      dcb.fParity                     = TRUE;
      dcb.fOutxCtsFlow                = TRUE;
      dcb.fOutxDsrFlow                = FALSE;
      dcb.fDtrControl                 = DTR_CONTROL_ENABLE;
      dcb.fDsrSensitivity             = FALSE;
      dcb.fTXContinueOnXoff           = TRUE;
      dcb.fOutX                       = FALSE;
      dcb.fInX                        = FALSE;
      dcb.fErrorChar                  = FALSE;
      dcb.fNull                       = FALSE;
      dcb.fRtsControl                 = RTS_CONTROL_HANDSHAKE;
      dcb.fAbortOnError               = FALSE;
      dcb.ByteSize                    = 8;
      dcb.Parity                      = NOPARITY;
      dcb.StopBits                    = ONESTOPBIT;

      ct.ReadIntervalTimeout          = MAXDWORD;
      ct.ReadTotalTimeoutMultiplier   = 0;
      ct.ReadTotalTimeoutConstant     = 0;
      ct.WriteTotalTimeoutMultiplier  = 0;
      ct.WriteTotalTimeoutConstant    = 3000;

      (void)PurgeComm(m_hBasePort, PURGE_RXCLEAR | PURGE_TXCLEAR);
      (void)SetCommState(m_hBasePort, &dcb);
      (void)SetCommTimeouts(m_hBasePort, &ct);
    }
  }

  return (m_hBasePort != INVALID_HANDLE_VALUE);
}

//////////////////////////////////////////////////////////////////////////////
//
// Close the opened base port.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::CloseBasePort(void) {
  CNTUSAGE(&m_RilHandleLock);
  if (m_hBasePort != INVALID_HANDLE_VALUE) {
    (void)CloseHandle(m_hBasePort);
    m_hBasePort = INVALID_HANDLE_VALUE;
  }
  return TRUE;
}


//////////////////////////////////////////////////////////////////////////////
//
// Prepares reopening of all port devices (see also ReopenAllPortDevices())
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::PrepareReopen( void ) {
    // the reopen event should be reset on suspend to ensure, that any thread
    // that checks for reopen (i.e. device ports reader threads) will wait
    // after resume until the RIL is informed about the new power state and
    // reopens all ports
    ResetEvent(m_hReopenEvent);
}

//////////////////////////////////////////////////////////////////////////////
//
// Close all port devices and open them again. That operation is necessary in
// case the underlaying USB driver is executing a reattach without the device
// being really closed (e.g. in case of an unintentional USB reset during the
// global resume process).
// To allow the function to execute without disturbing the RIL's command
// proocessing it must be called while the command handling is blocked.
//
// Params:
//  dwTimeout: The timeout in milliseconds we're trying to reopen the ports.
//
// Return:
//  FALSE if any kind of error occurred, TRUE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::ReopenAllPortDevices(DWORD dwTimeout) {
COM_PORT_TYPE  i;
DWORD          dwStart;
BOOL           fPortOpened[NUMBER_OF_PORTS];
BOOL           fPortReady[NUMBER_OF_PORTS];
BOOL           fRet = FALSE;

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    ResetEvent(m_hReopenEvent);
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Starting to reopen used ports\r\n")));
    for (i = 0; i < NUMBER_OF_PORTS; i++) {
      if (m_pComDevice[i]->IsDownstreamPortOpen()) {
        fPortOpened[i] = TRUE;
        m_pComDevice[i]->CloseDownstreamPort(TRUE);
      } else {
        fPortOpened[i] = FALSE;
      }
    }
    memset(fPortReady, 0, sizeof(fPortReady));
    dwStart = GetTickCount();
    do {
      fRet = TRUE;
      for (i = 0; i < NUMBER_OF_PORTS; i++) {
        if (fPortOpened[i] && !fPortReady[i]) {
          if(m_pComDevice[i]->OpenDownstreamPort(m_pRilHandle)) {
            m_pComDevice[i]->InitComPortForRIL(NULL, NULL);
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: \"%s\" has been successfully reopened\r\n"), GetDebugName(i)));
            fPortReady[i] = TRUE;
          } else {
            fRet = FALSE;
          }
        }
      }
      if (!fRet) {
        if (WaitForSingleObject(m_hReopenEvent, 100) != WAIT_TIMEOUT) {
          break;
        }
      }
    } while(!fRet && ((GetTickCount() - dwStart) < dwTimeout));
    SetEvent(m_hReopenEvent);
  }

  if (!fRet) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Failed to reopen all ports!\r\n")));
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Wait for the reopen process to finish.
 //
// Params:
//  dwTimeout: The timeout in milliseconds we're waiting for the reopen.
//
// Return:
//  TRUE if we had an ongoing reopen process, false otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::WaitReopen(DWORD dwTimeout) {
BOOL fRet;

  fRet = (WaitForSingleObject(m_hReopenEvent, 0) == WAIT_OBJECT_0) ? FALSE : TRUE;
  if (fRet) {
    (void)WaitForSingleObject(m_hReopenEvent, dwTimeout);
  }
  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the file handle of a given port.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  The file handle of the given port or INVALID_HANDLE_VALUE in case of an
//  error.
//
//////////////////////////////////////////////////////////////////////////////
HANDLE CComManager::GetPortHandle(COM_PORT_TYPE PortType) {
  CNTUSAGE(&m_RilHandleLock);
  if (IsValidPort(PortType) && PortType < NUMBER_OF_PORTS) {
    if (PortType == DATA_PORT) {
      if (m_hBasePort != INVALID_HANDLE_VALUE) {
        return m_hBasePort;
      }
    }
    if (m_pComDevice[PortType]) {
      return m_pComDevice[PortType]->GetPortHandle();
    }
  }
  return INVALID_HANDLE_VALUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Unlock a port device for usage by external applications.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  TRUE if the port device unlocked, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::UnlockPortDevice(COM_PORT_TYPE PortType) {
  if (!IsValidPort(PortType)) {
    return FALSE;
  }

  CNTUSAGE(&(m_PortLock[PortType]));

  if ((m_PortUsage[PortType] == USAGE_INTERNAL) && m_pComDevice[PortType]) {
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: \"%s\" has been unlocked for external usage\r\n"), GetDebugName(PortType)));
    m_PortUsage[PortType] = USAGE_DATA;
    Sleep(100);
  }

  return (m_PortUsage[PortType] == USAGE_DATA);
}

//////////////////////////////////////////////////////////////////////////////
//
// Lock a port device for exclusive internal usage.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  TRUE if the port device locked, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::LockPortDevice(COM_PORT_TYPE PortType, BOOL fReopen) {
  if (!IsValidPort(PortType)) {
    return FALSE;
  }

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    CNTUSAGE2(&(m_PortLock[PortType]));
    if ((m_PortUsage[PortType] != USAGE_INTERNAL) && m_pComDevice[PortType]) {
      RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: \"%s\" has been locked for exclusive internal usage\r\n"), GetDebugName(PortType)));
      m_PortUsage[PortType] = USAGE_INTERNAL;
      // Release all possibly pending IO functions and response threads
      DWORD    i, dwError;
      COMSTAT  tComStat;
      // Since the Read(), Write() and IOControl() functions of the data port called by any
      // application might block they must not be locked with the critical section to avoid
      // possible deadlocks. Although the check of the g_fApplComLocked flag at the start
      // of each function is locked we might have a race condition after this flag check and
      // the call of this disable function which is not avoidable. So the number of called IO
      // functions is counted with a global counter and we're repeatingly releasing them after
      // an additionial short delay until no more are pending. To avoid a lock of the driver in
      // case of any problem with the port hardware this is done no longer than 10 seconds.
      // The return value is dependant of a successful release of all pending IO functions.
      for (i = 0; i < 100; i++) {
        Sleep(50);
        if (!m_pComDevice[PortType]->VirtClearCommError(&dwError, NULL,fReopen)) break;
        (void)m_pComDevice[PortType]->VirtClearCommBreak(fReopen);
        if (!m_pComDevice[PortType]->VirtPurgeComm(PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_TXABORT|PURGE_RXABORT,fReopen)) break;
        if (!m_pComDevice[PortType]->VirtSetCommMask(0,fReopen)) break;
        Sleep(50);
        if (!m_pComDevice[PortType]->VirtClearCommError(&dwError, &tComStat,fReopen)) break;

        // We have to ensure that there are no bytes left in the modules send buffer left which
        // had been possibly blocked by flow control before!
        if (!m_dwIoCount[PortType] && !tComStat.cbInQue) {
          break;
        }
      }
      m_dwIoCount[PortType] = 0;
    }
    Sleep(100);
  }

  return (m_PortUsage[PortType] == USAGE_INTERNAL);
}

//////////////////////////////////////////////////////////////////////////////
//
// Execute ReadFile() calls from external applications.
//
// Params:
//  PortType: The port type of the port to be accessed.
//  ...     : The usual ReadFile parameters.
//
// Return:
//  The number of read bytes.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CComManager::ExternalRead(COM_PORT_TYPE PortType, LPVOID pBuf, DWORD len) {
DWORD dwRet = 0;

  CNTUSAGE(&(m_PortLock[PortType]));

  if (m_pComDevice[PortType] && (m_PortUsage[PortType] != USAGE_INTERNAL)) {
    if (RHA_IsRadioPwrOn()) {
      if ((len > 0) && pBuf) {
        m_dwIoCount[PortType]++;
        if (!m_pComDevice[PortType]->VirtRead((BYTE*)pBuf, len, dwRet)) {
          dwRet = (DWORD)-1;
        } else {
          m_pComDevice[PortType]->UpdateComStatData(TRUE, dwRet);
        }
        m_dwIoCount[PortType]--;
      }
    } else {
      // Module has been powered off
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExternalRead(%s) -Module has been powered off externally!\r\n"), GetDebugName(PortType)));
      m_PortUsage[PortType] = USAGE_INTERNAL;
      (void)GetRILDevice()->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
      (void)g_PowerManager.ForcePowerOff();
      SetLastError(ERROR_ACCESS_DENIED);
    }
  } else {
    SetLastError(ERROR_ACCESS_DENIED);
  }
#ifdef RIL_ALLOW_TRACE_PPP_DATA
  if (dwRet > 0 && dwRet != (DWORD)-1 && (g_dwDebugZones&ZONE_DATA) != 0 && m_fTraceExtData) {
      RILRetailTraceZone(ZONE_DATA, (TEXT("RilDrv: ExternalRead(%s): read %d bytes\r\n"), GetDebugName(PortType), dwRet));
      TraceBuffer((BYTE *)pBuf, dwRet);
  }
#endif
  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Execute WriteFile() calls from external applications.
//
// Params:
//  PortType: The port type of the port to be accessed.
//  ...     : The usual WriteFile parameters.
//
// Return:
//  The number of written bytes.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CComManager::ExternalWrite(COM_PORT_TYPE PortType, LPCVOID pBuf, DWORD len) {
DWORD  dwRet = 0;

  CNTUSAGE(&(m_PortLock[PortType]));

  if (m_pComDevice[PortType] && (m_PortUsage[PortType] != USAGE_INTERNAL)) {
    if (RHA_IsRadioPwrOn()) {
      m_dwIoCount[PortType]++;
      if (m_pComDevice[PortType]->VirtWrite(pBuf, len, dwRet)) {
        m_pComDevice[PortType]->UpdateComStatData(FALSE, dwRet);
      } else {
        dwRet = (DWORD)-1;
      }
      m_dwIoCount[PortType]--;
    } else {
      // Module has been powered off
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExternalWrite(%s) -Module has been powered off externally!\r\n"), GetDebugName(PortType)));
      m_PortUsage[PortType] = USAGE_INTERNAL;
      (void)GetRILDevice()->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
      (void)g_PowerManager.ForcePowerOff();
      SetLastError(ERROR_ACCESS_DENIED);
    }
  } else {
    SetLastError(ERROR_ACCESS_DENIED);
  }
#ifdef RIL_ALLOW_TRACE_PPP_DATA
  if ((g_dwDebugZones&ZONE_DATA) != 0 && m_fTraceExtData) {
    if (dwRet > 0 && dwRet != (DWORD)-1) {
      RILRetailTraceZone(ZONE_DATA, (TEXT("RilDrv: ExternalWrite(%s): write %d bytes\r\n"), GetDebugName(PortType), dwRet));
      TraceBuffer((BYTE *)pBuf, dwRet);
    }
    else {
      RILRetailTraceZone(ZONE_DATA, (TEXT("RilDrv: ExternalWrite(%s) failed to write %d bytes (return: %d)\r\n"), GetDebugName(PortType), len, dwRet));
    }
  }
#endif
  return dwRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Execute IoControl() calls from external applications.
//
// Params:
//  PortType: The port type of the port to be accessed.
//  ...     : The usual DeviceIoControl parameters.
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::ExternalIoControl(COM_PORT_TYPE PortType, DWORD dwCode, const PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut) {
BOOL   fRet = FALSE;
DWORD  dwOutUsed;

  CNTUSAGE(&(m_PortLock[PortType]));

  if (m_pComDevice[PortType] && (m_PortUsage[PortType] != USAGE_INTERNAL)) {
    if (RHA_IsRadioPwrOn()) {
      m_dwIoCount[PortType]++;
      if (m_pComDevice[PortType]->VirtIOControl(dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, dwOutUsed, (m_PortUsage[PortType] == USAGE_DATA) ? TRUE : FALSE)) {
        // Everything fine, copy used bytes
        if (pdwActualOut) {
          *pdwActualOut = dwOutUsed;
        }
        fRet = TRUE;
      }
      m_dwIoCount[PortType]--;
    } else {
      // Module has been powered off
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExternalIoControl(%s) - Module has been powered off externally!\r\n"), GetDebugName(PortType)));
      (void)GetRILDevice()->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
      m_PortUsage[PortType] = USAGE_INTERNAL;
      (void)g_PowerManager.ForcePowerOff();
      SetLastError(ERROR_ACCESS_DENIED);
    }
  } else {
    SetLastError(ERROR_ACCESS_DENIED);
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the port type of the port to be used for a specific gprs context id.
//
// Params:
//  PortType: The port type we're looking for.
//
// Return:
//  The port type or INVALID_PORT if there is no port available.
//
//////////////////////////////////////////////////////////////////////////////
COM_PORT_TYPE CComManager::GetPortFromContextId(DWORD dwContextId) {
COM_PORT_TYPE  i;
DWORD          d;

  if ((dwContextId <= 1)) {
    for (i = (NUMBER_OF_PORTS - 1), d = 0; i >= 0; i--) {
      CNTUSAGE(&(m_PortLock[i]));
      if ((m_PortInfo[i].dwCaps & PORT_CAPS_DATA) && m_pComDevice[i] && m_pComDevice[i]->IsDownstreamPortOpen()) {
        d++;
        if (d >= dwContextId) {
          RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Found port \"%s\" (%s) for Gprs-Context-Id %d\r\n"), GetPortName(i), GetDebugName(i), dwContextId));
          return i;
        }
      }
    }
  }
  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: No port found for Gprs-Context-Id %d!\r\n"), dwContextId));
  return INVALID_PORT;
}

//////////////////////////////////////////////////////////////////////////////
//
// Send the very first initialization commands to the given port.
//
// Params:
//  PortType: The port to use.
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::SendPreInitCmds(COM_PORT_TYPE PortType) {
char   szResponse[MAX_DEFAULT_INIT_AT_RESPONSE];
DWORD  i;
BOOL   fRet = FALSE;

  CNTUSAGE(&m_RilHandleLock);
  if (m_pRilHandle) {
    fRet = TRUE;
    for (i = 0; (i < NUMBER_OF_PREINIT_CMDS) && fRet; i++) {
      if (!m_pRilHandle->ExSendATCmdGetRsp(PortType, (char*)g_szPreInitString[i], szResponse, sizeof(szResponse), m_pRilHandle->GetPreMuxInitTimeout(), 1)) {
        fRet = FALSE;
      }
    }
  }

  return fRet;
}

#define SEND_INIT_STRING_LENGTH    1024

//////////////////////////////////////////////////////////////////////////////
//
// Send the pre-PIN initialization commands to all ports.
//
// Params:
//  fFinalInit: Flag indicating if CMDOPT_FINALINIT should be added to the
//              last command ton continue RIL initialization (TRUE) or not
//              to send the init commands only (FALSE).
//
// Return:
//  TRUE onsuccess, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::SendInitCmds(BOOL fPreInitOnly) {
char              szInit[SEND_INIT_STRING_LENGTH];
char              szCmd[MAX_ATCMD_LEN];
WCHAR             wszCmd[MAX_ATCMD_LEN];
char*             pszStart;
char*             pszEnd;
DWORD             dwIndex;
DWORD             fTW;
INITSTRING_DATA*  pInitData;
BOOL              fRet = TRUE;

  // Step through all port devices
  CNTUSAGE(&m_RilHandleLock);

  memset(wszCmd, 0, sizeof(wszCmd));

  // Get the init commands
  pInitData = NULL;
  if (g_rgisdModuleInit[g_dwModemType][0].szCmd) {
    pInitData = g_rgisdModuleInit[g_dwModemType];
  }
  if (!pInitData) {
    if (g_rgisdModuleInit[MODEMTYPE_UNKNOWN][0].szCmd) {
      pInitData = g_rgisdModuleInit[MODEMTYPE_UNKNOWN];
    }
    if (!pInitData) {
      // No init commands found, step through to next port
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to find Pre-Pin-Initialization\r\n")));
      return FALSE;
    }
  }

  // Now step through the init commands
  for (dwIndex = 0; (dwIndex < NUMBER_OF_PREPIN_ENTRIES) && pInitData[dwIndex].szCmd; dwIndex++) {
    szInit[0]=TEXT('\0');

    if (pInitData[dwIndex].szCmd[0]) {
      strncatz(szInit, pInitData[dwIndex].szCmd, sizeof(szInit));
    }

    // Now go through the string and break it up into individual commands separated by a '|'
    pszStart = szInit;
    for (;;) {
      // Look for the end of the current command
      pszEnd = strchr(pszStart, '|');
      if (pszEnd) {
        // If we found a termination char, terminate the command there
        *pszEnd = '\0';
      }

      UINT nRetriesOnError = 0;
      UINT nRetryOnErrorDelay = 0;
      const char* pszRetry = strchr(pszStart, '\\');
      if (pszRetry) {
        char *pszOpt = (char *)pszRetry++;
        if (! ParseUInt(pszRetry, FALSE, nRetriesOnError, pszRetry)     ||
            ! MatchStringBeginning(pszRetry, ",", pszRetry)             ||
            ! ParseUInt(pszRetry, FALSE, nRetryOnErrorDelay, pszRetry)  ||
            ! MatchStringBeginning(pszRetry, "\\", pszRetry)) {
          nRetriesOnError = 0;
          nRetryOnErrorDelay = 0;
        } else {
          *pszOpt = '\0';
        }
      }

      if (!(pInitData[dwIndex].dwCmdOption & CMDOPT_GENERALINIT) || !fPreInitOnly) {
        // Send the command
        nRetriesOnError = max(2, nRetriesOnError);
        _snprintfz(szCmd, MAX_ATCMD_LEN, "AT%s\r", pszStart);
        if (!QueueInternalCmd(pInitData->CmdMode, m_pRilHandle, szCmd, pInitData[dwIndex].dwCmdOption | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, nRetriesOnError, nRetryOnErrorDelay)) {
          // Couldn't send an init string -- signal critical error
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: No memory for new response (init)!")));
          SignalCriticalError();
          fRet = FALSE;
          break;
        }
      }

      // If this was the last command, get out now
      if (!pszEnd) {
        break;
      }

      // Get the next command
      pszStart = pszEnd + 1;
    }
  }

  // Finally initialize the correct audio path
  if (fRet && !fPreInitOnly && !g_fNoAudioSettings && !UNSUPPORTS_AUDIO) {
    // Initialize all audio interfaces.
    fRet = m_pRilHandle->InitializeAudioSettings();
  }

  // Enable temperature warnings, if configured.
  if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EnableTemperatureWarning"), &fTW) && (fTW>=0 && fTW<=1)) {
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCTM=%u\r", fTW);
    (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilHandle, szCmd, CMDOPT_NONE, APIID_NONE, g_TimeoutCmdInit, 0, 0);
  }

  // Additionally make URC ringline configuration, if applicable.
  if (fRet && !fPreInitOnly && SUPPORTS_URC_CONFIGURATION) {
    DWORD dwUrcConfig = 1;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UrcRinglineCfg"), &dwUrcConfig)) {
      const char *szOption = NULL;

      switch (dwUrcConfig) {
        case 0:
            szOption = "off";
            break;
        case 1:
            szOption = "local";
            break;
        case 2:
            szOption = "asc0";
            break;
        case 3:
            szOption = "wakeup";
            break;
        default:
            szOption = NULL;
      }

      if (szOption != NULL) {
        _snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=URC/Ringline,\"%s\"\r", szOption);
        (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
      }
    }
  }

  // Optional customer defined AT command
  if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("PreInitCommand"), wszCmd, MAX_ATCMD_LEN)) {
        _snprintfz(szCmd, MAX_ATCMD_LEN, "%S\r", wszCmd);
        (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilHandle, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
  }
  memset(wszCmd, 0, sizeof(wszCmd));
  if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("PreInitCommandURC"), wszCmd, MAX_ATCMD_LEN)) {
        _snprintfz(szCmd, MAX_ATCMD_LEN, "%S\r", wszCmd);
        (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilHandle, szCmd, CMDOPT_INIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
  }

  // Finally send our final init marker to finish initialization
  if (fRet && !fPreInitOnly) {
    if (!QueueInternalCmd(HCCMD_STANDARD, m_pRilHandle, NULL, CMDOPT_NOOP | CMDOPT_INIT | CMDOPT_FINALINIT | CMDOPT_GENERALINIT | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, 0, 0)) {
      fRet = FALSE;
    }
  }

  return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Send the post-PIN initialization commands to all ports.
//
// Return:
//  TRUE on success, FALSE on failure.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CComManager::SendUnlockedInitCmds(void) {
DWORD             dwIndex;
INITSTRING_DATA*  pInitData;
BOOL              fRet = FALSE;

  // First lock all port devices
  CNTUSAGE(&m_RilHandleLock);

  // Get the init commands
  pInitData = NULL;
  if (g_szUnlockedInitString[g_dwModemType][0].szCmd) {
    pInitData = g_szUnlockedInitString[g_dwModemType];
  }
  if (!pInitData) {
    if (g_szUnlockedInitString[MODEMTYPE_UNKNOWN][0].szCmd) {
      pInitData = g_szUnlockedInitString[MODEMTYPE_UNKNOWN];
    }
    if (!pInitData) {
      // No init commands found, step through to next port
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to find Post-Pin-Initialization\r\n")));
      return FALSE;
    }
  }

  if (m_pRilHandle) {
    // Send the unlocked init strings
    fRet = TRUE;
    for (dwIndex = 0; (dwIndex < NUMBER_OF_POSTPIN_ENTRIES) && pInitData[dwIndex].szCmd && pInitData[dwIndex].szCmd[0]; dwIndex++) {
      // Place the command into the command queue (this may block if the queue is full)
      if (!QueueInternalCmdBlocking(pInitData[dwIndex].CmdMode, m_pRilHandle, pInitData[dwIndex].szCmd, pInitData[dwIndex].dwCmdOption, APIID_UNLOCKPHONE, 0, 0, 0)) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Unable to queue Post-Pin-Init CCommand\r\n")));
        fRet = FALSE;
      }
    }
  }

  return fRet;
}

#ifdef RIL_ALLOW_TRACE_PPP_DATA
//////////////////////////////////////////////////////////////////////////////
//
// Starts and ends the tracing of PPP data.
//
// Params:
//  fTraceExtData: TRUE=trace enabled
//
// Return:
//  (none)
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::TraceExtData(BOOL fTraceExtData, BOOL fCSDConnection)  {
  m_dwTraceExtDataCounterMax = DEFAULT_MAX_TRACE_DATA_COUNTER;
  (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("MaxExtDataTracePackets"), &m_dwTraceExtDataCounterMax);
  m_dwTraceExtDataCounter = 0;
  m_fTraceExtData = fTraceExtData;
  m_fCSDConnection = fCSDConnection;
  return;
}

//////////////////////////////////////////////////////////////////////////////
//
// Called, when the module sends the "+CIEV: call,1" URC.
// On GPRS connections this URC indicates, that the context is now active, so
// usually this is a good point to stop running PPP traces.
// For CSD connections the notification should be ignored.
//
// Params:
//  fTraceExtData: TRUE=trace enabled
//
// Return:
//  (none)
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::TraceExtDataCall1()  {
DWORD dwCall1Trigger = 0;

  // If CSD connection: ignore this notification,
  if (m_fCSDConnection)
    return;
  // GPRS connection: unless it is configured otherwise, stop trace now.
  (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("ExtDataTraceEndCall1"), &dwCall1Trigger);
  if (dwCall1Trigger == 1)
    TraceExtData(FALSE, FALSE);
  return;
}

//////////////////////////////////////////////////////////////////////////////
//
// Writes a block of data to the RIL log file.
//
// Params:
//  pBufC: Buffer containing the data block
//  dwLen: Number of bytes in the buffer
//
// Return:
//  (none)
//
//////////////////////////////////////////////////////////////////////////////
void CComManager::TraceBuffer(BYTE *pBufC, DWORD dwLen) {
  TCHAR   Buf[1024];
  TCHAR*  pBufT = Buf;
  BOOL    tr = FALSE;
  for (DWORD i = 1; i <= dwLen; i++) {
#ifdef RILDRV_CE6
    _stprintf_s(pBufT, 1024 - (pBufT - Buf), TEXT("0x%02X "), pBufC[i-1]);
#else
    _stprintf(pBufT, TEXT("0x%02X "), pBufC[i-1]);
#endif
    pBufT += 5;
    if (i && !(i % 16)) {
#ifdef RILDRV_CE6
      swprintf_s(pBufT, 1024 - (pBufT - Buf), TEXT("\r\n"));
#else
      swprintf(pBufT, TEXT("\r\n"));
#endif
      RILRetailTraceZone(ZONE_DATA, (Buf));
      pBufT = Buf;
      tr = TRUE;
    } else {
      tr = FALSE;
    }
  }
  if (!tr) {
#ifdef RILDRV_CE6
    _stprintf_s(pBufT, 1024 - (pBufT - Buf), TEXT("\r\n"));
#else
    _stprintf(pBufT, TEXT("\r\n"));
#endif
    RILRetailTraceZone(ZONE_DATA, (Buf));
  }
  m_dwTraceExtDataCounter ++;
  if (m_dwTraceExtDataCounter >= m_dwTraceExtDataCounterMax)
    m_fTraceExtData = FALSE;
  return;
}
#endif
