/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/GpsHand.cpp $
 * $Revision: /main/ms-d/47 $
 *     $Date: 2013-05-23 11:56:04 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/GpsHand.cpp$ 
 * 
 * /main/ms-d/47   2013-05-23 11:56:04   pmackent 
 * Handle AT^SCFG and AT^SGPSC in one function
 *
 * /main/ms-d/46   2013-05-14 16:27:01   pmackent
 * GPS/GLONASS setting need reboot; ensure GPS antenna is on auto-mode
 *
 * /main/ms-d/45   2013-04-25 13:18:41   pmackent
 * No GPS AT commands on emergency calls (IPIS100087126)
 *
 * /main/ms-d/44   2012-10-24 10:52:45   pmackent
 * Additional configurable delay after A-GPS upload
 *
 * /main/ms-d/43   2012-10-22 11:18:00   pmackent
 * Additional configurable delay after AGPS download
 *
 * /main/ms-d/42   2012-10-22 10:07:31   pmackent
 * Allow to configure retries for AT^SGPSC=Engine
 *
 * /main/ms-d/41   2012-10-19 10:10:40   pmackent
 * Workaround for A-GPS timing problem (IPIS100082936)
 *
 * /main/ms-d/40   2012-10-08 16:18:26   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/39   2012-09-24 09:56:54   pmackent
 * Slight change in A-GPS download algorithm
 *
 * /main/ms-d/38   2012-09-21 16:38:29   pmackent
 * Check for valid A-GPS file with AT^SGPSC=Info
 *
 * /main/ms-d/37   2012-09-18 09:26:05   pmackent
 * Use AT^SGPSC instead of AT^SGPSS
 *
 * /main/ms-d/36   2012-08-13 16:02:26   pmackent
 * New AHS3 GPS features (Glonass, dead reckoning)
 *
 * /main/ms-d/35   2012-08-13 11:06:39   pmackent
 * Apply changes to NMEA frequency immediately (IPIS100081354)
 *
 * /main/ms-d/34   2012-08-09 13:46:27   pmackent
 * Fix potential memory leaks
 *
 * /main/ms-d/33   2012-03-23 10:50:21   ctna0256
 * Registry key to adjust NMEA output frequency
 *
 * /main/ms-d/32   2012-02-22 13:26:41   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/31   2011-10-24 15:06:12   ctna0256
 * Move AGPS test mode command
 *
 * /main/ms-d/30   2011-09-12 14:50:31   ctna0256
 * New GPS test mode for PH8 R2
 *
 * /main/ms-d/29   2011-09-12 10:55:40   ctna0256
 * New NMEA buffer mode
 *
 * /main/ms-d/28   2011-07-22 16:25:58   ctna0256
 * Notification about GPS activities
 *
 * /main/ms-d/27   2011-04-01 11:29:52   ctna0256
 * Adaptions for new GPS configuration parameters
 *
 * /main/ms-d/26   2011-03-09 14:24:43   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/25   2011-01-18 15:06:20   ctna0256
 * New syntax for loading A-GPS files
 *
 * /main/ms-d/24   2010-12-15 10:43:52   ctna0256
 * Performed lint check
 *
 * /main/ms-d/23   2010-11-26 11:46:15   ctna0256
 * Add power saver for GPS
 *
 * /main/ms-d/22   2010-11-05 09:00:07   ctna0256
 * Make 64-bit operation ARM compatible
 *
 * /main/ms-d/21   2010-11-04 15:58:47   ctna0256
 * New registry key for AGPS file age
 *
 * /main/ms-d/20   2010-09-01 15:55:32   ctna0256
 * AGPS under CE6
 *
 * /main/ms-d/19   2010-08-25 11:09:25   ctna0256
 * Make code PreFast compatible
 *
 * /main/ms-d/18   2010-08-24 10:47:25   ctna0256
 * Improved AGPS file download
 *
 * /main/ms-d/16   2010-08-20 10:41:28   ctna0256
 * Prevent deadlock between power manager and GPS handling
 *
 * /main/ms-d/15   2010-08-19 14:29:59   ctna0256
 * Implementation A-GPS for PH8
 *
 * /main/ms-d/14   2008-12-19 10:22:43   ctna0256
 * Increase priority of enable passiv GPS command.
 *
 * /main/ms-d/13   2008-08-22 10:23:55   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/12   2008-08-21 09:34:32   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/11   2008-07-09 13:32:38   kr36712
 * -deadlock in conjunction with QueueInternalCmdBlocking() inside of
 *  SwitchGpsStatePassive()
 * -> changed to (non blocking)  QueueInternalCmd()
 *
 * /main/ms-d/10   2008-04-29 13:39:55   mp49210
 * Removed Lint warning
 *
 * /main/ms-d/9   2008-04-16 16:24:12   mp49210
 * Debug zones for RHA traces
 *
 * /main/ms-d/8   2008-04-03 14:41:37   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/7   2008-02-11 09:18:16   mp49210
 * Remove LINT warnings
 *
 * /main/ms-d/6   2008-01-24 15:57:38   kr36712
 * integration GPS for MTLR and emergency call
 *
 * /main/ms-d/5   2007-12-19 15:14:22   mp49210
 * Optional trace of NMEA data
 *
 * /main/ms-d/4   2007-12-14 11:35:21   kr36712
 * GPS Power Management
 *
 * /main/ms-d/3   2007-11-27 18:06:14   mp49210
 * Lint compatibility
 *
 * /main/ms-d/2   2007-11-26 17:05:44   kr36712
 * integration of GPS support
 * use of the mux protocol
 *
 * /main/ms-d/1   2006-10-25 16:53:37   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"

#ifndef RIL_AGPS_DOWNLOAD_WITH_EXE
#include <urlmon.h>
#endif

#ifndef _PREFAST_
#pragma warning(disable: 4068)
#endif

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

CGpsHandling g_GpsHandling;

#define NMEA_TRACE_BUFFER_SIZE    1024

#define AGPS_TEMP_KEY_SIZE        40

#define AGPS_STARTMODE_NO_DATA    1
#define AGPS_STARTMODE_WITH_DATA  0

char gCurrentAPN[MAX_ATCMD_LEN];
bool fToSetAGPS = FALSE;

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

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CGpsHandling::CGpsHandling() {
  InitializeCriticalSection(&m_csLock);
  m_hCloseEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_hLockEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  m_hAGPSUploadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  m_hAGPSDownloadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  m_hNmeaRegChangeThreadCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  m_pRilDevice = 0;
  m_hPort = INVALID_HANDLE_VALUE;
  m_fGpsPassive = FALSE;
  m_dwEngineStatus = 0;
  memset(&m_tLastDownload, 0, sizeof(FILETIME));
  m_fNmeaRegChangeThreadRunning = FALSE;
  m_fConfigureGpsOnPassiveMode = FALSE;
  m_fAssistGPSConnection = FALSE;
  m_hNmeaRegChangeThread = CreateThread(NULL, 0, NmeaRegChangeThread, (LPVOID)this, 0, NULL);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CGpsHandling::~CGpsHandling() {
  (void)Deinit();
  CloseHandle(m_hLockEvent);
  CloseHandle(m_hCloseEvent);
  CloseHandle(m_hAGPSUploadEvent);
  CloseHandle(m_hAGPSDownloadEvent);
  DeleteCriticalSection(&m_csLock);
  if (m_fNmeaRegChangeThreadRunning) {
    // Signal thread to be close itself.
    SetEvent(m_hNmeaRegChangeThreadCloseEvent);
    Sleep(1000);
#ifdef RILDRV_CE6
// TerminateThread cannot be avoided here
#pragma warning (push)
#pragma warning ( disable : 6258 )
#else
#pragma prefast(push)
#pragma prefast(disable: 258, "TerminateThread cannot be avoided here")
#endif
    // Is the thread still running? Use TerminateThread as an emergency exit.
    if (m_fNmeaRegChangeThreadRunning)
      TerminateThread(m_hNmeaRegChangeThread, (DWORD)-1);
#ifdef RILDRV_CE6
#pragma warning (pop)
#else
#pragma prefast(pop)
#endif
    CloseHandle(m_hNmeaRegChangeThread);
    CloseHandle(m_hNmeaRegChangeThreadCloseEvent);
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize class with required information.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::Init(CRilHandle* pRilDevice) {
  SYNCBLOCK(m_csLock);
  if (!pRilDevice) return FALSE;
  m_pRilDevice = pRilDevice;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the GPS handling.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::Deinit(void) {
  SYNCBLOCK(m_csLock);
  ForceDeactivate();
  m_pRilDevice = 0;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the GPS handling without sending any AT commands
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
void CGpsHandling::ForceDeactivate(void) {
  SYNCBLOCK(m_csLock);
  if (m_hPort != INVALID_HANDLE_VALUE) {
    CloseHandle(m_hPort);
    m_hPort = INVALID_HANDLE_VALUE;
  }
  ResetEvent(m_hLockEvent);
  SetEvent(m_hCloseEvent);
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread to execute ForceDeactivate asynchron.
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI GPSForceDeactivateThread(LPVOID pVoid)
{
    CGpsHandling* pHandle = (CGpsHandling *)pVoid;

    pHandle->ForceDeactivate();

    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the GPS handling without sending any AT commands asynchron. This is necessary
// to prevent a dead-lock when the module shuts down during the executions of a GPS
// command.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
void CGpsHandling::ForceDeactivateAsync(void) {
    CloseHandle(CreateThread(NULL, 0, GPSForceDeactivateThread, (LPVOID)this, 0, 0));
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread to execute SwitchGpsStatePassive asynchron.
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI GPSDisableGpsStatePassiveThread(LPVOID pVoid)
{
    CGpsHandling* pHandle = (CGpsHandling *)pVoid;

    (void)pHandle->SwitchGpsStatePassive(FALSE);

    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Disable GPS passive state asynchron. This is necessary to prevent a
// dead-lock when the module shuts down during the executions of a GPS
// command.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
void CGpsHandling::DisableGpsStatePassiveAsync(void) {
    CloseHandle(CreateThread(NULL, 0, GPSDisableGpsStatePassiveThread, (LPVOID)this, 0, 0));
}

//////////////////////////////////////////////////////////////////////////////
//
// Removes local copy of AGPS file.
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::RemoveAGPSFile() const {
    WCHAR wszFileName[MAX_PATH];
    WCHAR wszTempPath[MAX_PATH];

    memset(wszFileName, 0, sizeof(wszFileName));
    memset(wszTempPath, 0, sizeof(wszTempPath));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPStempPath"), wszTempPath, MAX_PATH))
        wcscpy(wszTempPath, AGPS_DEFAULT_PATH);
    (void)_snwprintfz(wszFileName, MAX_PATH, L"%s%s", wszTempPath, AGPS_FILE_NAME);

    return DeleteFile(wszFileName);
}

//////////////////////////////////////////////////////////////////////////////
//
// Checks, if downloaded AGPS file is a valid AGPS file.
//
// Parameters:
//     wszFilename [in]: file to be tested
//     pdwFileSize [out]: length of the file to be tested
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::CheckAGPSFile(WCHAR *wszFilename, DWORD *pdwFileSize) const {
    HANDLE hFile;
    BOOL   fRet = FALSE;

    // First check, if the file exists and can be opened.
    hFile = CreateFile(wszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE) {
        DWORD dwFileSize;

        // Check if file is not empty or too large to handle.
        dwFileSize = GetFileSize(hFile, NULL);
        if (pdwFileSize != NULL)
            *pdwFileSize = dwFileSize;
        if (dwFileSize > 0 && dwFileSize <= MAX_AGPS_FILE_LEN) {
            PBYTE  pBuffer;

            pBuffer = (PBYTE)AllocBlob(dwFileSize);
            if (pBuffer != NULL) {
                DWORD dwRead = 0;

                // Check if file can be read fully.
                if (!ReadFile(hFile, pBuffer, dwFileSize, &dwRead, NULL))
                    dwRead = 0;
                if (dwRead == dwFileSize) {
                    // Analyze file: if might be a "file not found" message in XML or HTML
                    // format instead of an actual AGPS file.
                    if (_strnicmp((char *)pBuffer, "<!DOCTYPE", 9) != 0 && _strnicmp((char *)pBuffer, "<?xml ", 6) != 0)
                        fRet = TRUE;
                    (void)FreeBlob(pBuffer);
                }
            }
        }

        if (!fRet)
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: invalid AGPS file detected\r\n")));

        CloseHandle(hFile);
    }

    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread to execute the download of the AGPS file. If will set an event, when finished.
// There are two ways to do this, depending on the build option RIL_AGPS_DOWNLOAD_WITH_EXE.
//
// RIL_AGPS_DOWNLOAD_WITH_EXE is not set:
// The RIL will download the file by itself from the internet using the API
// URLDownloadToFile(). This is the default method for Windows Mobile.
//
// RIL_AGPS_DOWNLOAD_WITH_EXE is set:
// The RIL will start an external executable, which is performing the download from
// the internet.
//
//////////////////////////////////////////////////////////////////////////////
#ifdef RIL_AGPS_DOWNLOAD_WITH_EXE
#ifdef RILDRV_CE6
// Visual Studio sees a missing parameter for the CreateProcess() function,
// which is only mandatory in Windows XP.
#pragma warning (push)
#pragma warning ( disable : 6309 6387 )
#endif
DWORD WINAPI GPSDownloadThread(LPVOID pVoid)
{
    PROCESS_INFORMATION sInfo;
    PAGPS_DOWNLOAD_PARAMS pParams = (PAGPS_DOWNLOAD_PARAMS)pVoid;
    AGPS_DOWNLOAD_PARAMS sParams;
    WCHAR wszAGPSexecutable[MAX_PATH];
    DWORD dwTimeout = AGPS_DEFAULT_DOWNLOAD_TIMEOUT;

    memcpy(&sParams, pParams, sizeof(AGPS_DOWNLOAD_PARAMS));
    delete pParams;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSDownloadTimeout"), &dwTimeout))
        dwTimeout = AGPS_DEFAULT_DOWNLOAD_TIMEOUT;

    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSDownloadExe"), wszAGPSexecutable, MAX_PATH))
        wcscpy(wszAGPSexecutable, AGPS_DEFAULT_DOWNLOAD_EXE);

    // Start the executable, which will download the AGPS file. The
    // executable is supposed to signal, when it finished (on success
    // and on failure).
    if (CreateProcess(wszAGPSexecutable, NULL, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &sInfo)) {
        if (WaitForSingleObject(sInfo.hProcess, NUM_AGPS_URLS * dwTimeout) != WAIT_OBJECT_0)    // Timeout: NUM_AGPS_URLS URLs might be read
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: %s timed out\r\n"), wszAGPSexecutable));
        CloseHandle(sInfo.hProcess);
        CloseHandle(sInfo.hThread);
    }

    // Set the event even when download failed. the RIL will check the file anyhow
    // before it is used.
    SetEvent(sParams.hDownloadEvent);

    return 0;
}
#ifdef RILDRV_CE6
#pragma warning (pop)
#endif
#else
DWORD WINAPI GPSDownloadThread(LPVOID pVoid)
{
    PAGPS_DOWNLOAD_PARAMS pParams = (PAGPS_DOWNLOAD_PARAMS)pVoid;
    AGPS_DOWNLOAD_PARAMS  sParams;

    memcpy(&sParams, pParams, sizeof(AGPS_DOWNLOAD_PARAMS));
    delete pParams;

    // Download the file from the internet.
    (void)URLDownloadToFile(NULL, sParams.wszURL, sParams.wszFile, 0, NULL);

    // Set the event even when download failed. the RIL will check the file anyhow
    // before it is used.
    SetEvent(sParams.hDownloadEvent);

    return 0;
}
#endif

//////////////////////////////////////////////////////////////////////////////
//
// Parsing function for the AT^SGPSC=Info,xtra command. The <duration>
// parameter is returned to (DWORD *)pParam.
//
//////////////////////////////////////////////////////////////////////////////
static HRESULT ParseSGPSInfoXtra(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    char szTemp[MAX_ATCMD_LEN];
    char szDuration[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    DWORD *pdwDuration = (DWORD *)pParam;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>^SGPSC: "Info","Xtra",<Week>,<Minute>,<Duration>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "^SGPSC: \"Info\",\"xtra\",", szRsp) ||
        !ParseString(szRsp, szTemp, MAX_ATCMD_LEN, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseString(szRsp, szTemp, MAX_ATCMD_LEN, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseString(szRsp, szDuration, MAX_ATCMD_LEN, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (pdwDuration != NULL) {
        *pdwDuration = atoi(szDuration);
    }

Error:
    return hr;
}

//////////////////////////////////////////////////////////////////////////////
//
// Checks how long ago the last download has been. If has been too long,
// a new download is necessary.
//
// Return:
//  TRUE if new download is needed, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::NeedAGPSDownload() const {
    DWORD dwHours = 0;
    BOOL  fNeedDownload = TRUE;

    if (NEW_AGPS_CONFIG_OPTIONS) {
        DWORD dwDuration = 0;

        // Check the remaining validity periode of the A-GPS file currently in the module.
        if (QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Info\",\"xtra\"\r", CMDOPT_NONE, APIID_NONE, 5000, 0, 0, ParseSGPSInfoXtra, NULL, (LPVOID)&dwDuration)) {
            if (dwDuration == 0) {
                // If remaining duration is 0, this means there is no valid file currently
                // in the module. The RIL must perform a download in any case.
                return TRUE;
            }
        }
        // Since there is a valid A-GPS file, the download can be skipped unless
        // AGPSMaxFileAgeHours is defined.
        fNeedDownload = FALSE;
    }
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSMaxFileAgeHours"), &dwHours)) {
        ULONGLONG ulCurrentTime;
        ULONGLONG ulLastDownload;
        ULONGLONG ulDiffSec;
        FILETIME ftCurrentTime;

        ulLastDownload = (ULONGLONG)(m_tLastDownload.dwLowDateTime) + ((ULONGLONG)(m_tLastDownload.dwHighDateTime) << 32);
        GetCurrentFT(&ftCurrentTime);
        ulCurrentTime = (ULONGLONG)(ftCurrentTime.dwLowDateTime) + ((ULONGLONG)(ftCurrentTime.dwHighDateTime) << 32);
        ulDiffSec = (ulCurrentTime - ulLastDownload) / 10000000L;
        if ((DWORD)ulDiffSec < dwHours * 3600) {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: download AGPS file skipped (age: %d s)\r\n"), (DWORD)ulDiffSec));
            fNeedDownload = FALSE;
        }
        else {
            fNeedDownload = TRUE;
        }
    }
    return fNeedDownload;
}

//////////////////////////////////////////////////////////////////////////////
//
// Gets the AGPS file from server. This file will be temporarily stored in the
// directory specified with the registry key "AGPStempPath".
// It will download from the URL specified with "AGPSdownloadURL". If this
// doesn't succeed, ith will try the backups "AGPSdownloadBackupURL1",
// "AGPSdownloadBackupURL2".
//
// Params:
//   szFilename [out]: if an AGPS file is available, the filename (including path) is place here.
//                     szFilename must hold MAX_PATH characters.
//   dwFileSize [out]: size of the AGPS file.
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::GetAGPSFile(char *szFilename, DWORD *pdwFileSize) {
    BOOL  fAGPSFileChecked = FALSE;
    WCHAR wszFileName[MAX_PATH];
    WCHAR wszTempPath[MAX_PATH];
    BOOL  fRet = FALSE;
#ifdef RIL_AGPS_DOWNLOAD_WITH_EXE
    DWORD dwTimeout = AGPS_DEFAULT_DOWNLOAD_TIMEOUT;
    PAGPS_DOWNLOAD_PARAMS pParam;

    memset(wszFileName, 0, sizeof(wszFileName));
    memset(wszTempPath, 0, sizeof(wszTempPath));

    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPStempPath"), wszTempPath, MAX_PATH))
        wcscpy(wszTempPath, AGPS_DEFAULT_PATH);
    (void)_snwprintfz(wszFileName, MAX_PATH, L"%s%s", wszTempPath, AGPS_FILE_NAME);

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSDownloadTimeout"), &dwTimeout))
        dwTimeout = AGPS_DEFAULT_DOWNLOAD_TIMEOUT;

    pParam = new AGPS_DOWNLOAD_PARAMS;
    if (pParam != NULL) {
        // Fill parameter struct
        wcsncpyz(pParam->wszFile, wszFileName, MAX_PATH);
        pParam->hDownloadEvent = m_hAGPSDownloadEvent;
        ResetEvent(m_hAGPSDownloadEvent);
        // Create thread, which will download the file
        CloseHandle(CreateThread(NULL, 0, GPSDownloadThread, (LPVOID)pParam, 0, 0));
        // Now wait for download to be finished
        if (WaitForSingleObject(m_hAGPSDownloadEvent, 100 + (NUM_AGPS_URLS * dwTimeout)) == WAIT_OBJECT_0) {
            if (CheckAGPSFile(wszFileName, pdwFileSize)) {
                fAGPSFileChecked = TRUE;
            }
        }
    }
#else
    WCHAR wszURL[NUM_AGPS_URLS][MAX_PATH];
    UINT  nIdx = 0;

    memset(wszFileName, 0, sizeof(wszFileName));
    memset(wszTempPath, 0, sizeof(wszTempPath));
    memset(wszURL, 0, sizeof(wszURL));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPStempPath"), wszTempPath, MAX_PATH))
        wcscpy(wszTempPath, AGPS_DEFAULT_PATH);
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSdownloadURL"), wszURL[0], MAX_PATH))
        wcscpy(wszURL[0], AGPS_DEFAULT_URL);
    for (nIdx = 1 ; nIdx < NUM_AGPS_URLS; nIdx++) {
        WCHAR wszKey[AGPS_TEMP_KEY_SIZE];

        (void)_snwprintfz(wszKey, AGPS_TEMP_KEY_SIZE, L"AGPSdownloadBackupURL%d", nIdx);
        (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, wszKey, wszURL[nIdx], MAX_PATH);
    }
    (void)_snwprintfz(wszFileName, MAX_PATH, L"%s%s", wszTempPath, AGPS_FILE_NAME);

    // I no default URL is given, don't try any download.
    if (wcslen(wszURL[0]) > 0) {
        nIdx = 0;

        (void)DeleteFile(wszFileName);
        // Try default and all backup URLs fro download
        while (nIdx < NUM_AGPS_URLS && wcslen(wszURL[nIdx]) > 0 && !fAGPSFileChecked) {
            DWORD dwTimeout = AGPS_DEFAULT_DOWNLOAD_TIMEOUT;
            PAGPS_DOWNLOAD_PARAMS pParam;

            if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSDownloadTimeout"), &dwTimeout))
                dwTimeout = AGPS_DEFAULT_DOWNLOAD_TIMEOUT;

            pParam = new AGPS_DOWNLOAD_PARAMS;
            if (pParam != NULL) {
                // Fill parameter struct
                (void)wcsncpyz(pParam->wszFile, wszFileName, MAX_PATH);
                (void)wcsncpyz(pParam->wszURL, wszURL[nIdx], MAX_PATH);
                pParam->hDownloadEvent = m_hAGPSDownloadEvent;
                ResetEvent(m_hAGPSDownloadEvent);
                // Create thread, which will download the file
                CloseHandle(CreateThread(NULL, 0, GPSDownloadThread, (LPVOID)pParam, 0, 0));
                // Now wait for download to be finished
                if (WaitForSingleObject(m_hAGPSDownloadEvent, dwTimeout) == WAIT_OBJECT_0) {
                    if (CheckAGPSFile(wszFileName, pdwFileSize)) {
                        fAGPSFileChecked = TRUE;
                    }
                }
                else {
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: load AGPS file %s timed out\r\n"), wszURL[nIdx]));
                }
            }

            nIdx++;
        }
    }
#endif

    // Check if file exists. Even if the download above was skipped, an
    // external application may have provided an AGPS file.
    if (fAGPSFileChecked || CheckAGPSFile(wszFileName, pdwFileSize)) {
        (void)_snprintfz(szFilename, MAX_PATH, "%S", wszFileName);
        fRet = TRUE;
    }
    return fRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// For modules with A-GPS support only: load A-GPS file from the internet and load it
// into the module
//
// Params: (none)
//
// Return: (none)
//
//////////////////////////////////////////////////////////////////////////////
void CGpsHandling::LoadAGPSParameters() {
    char  szCmd[MAX_ATCMD_LEN];
    DWORD dwTestModus = 0;

    // The GPS test modus is only available for all radios which support A-GPS.
    if (SUPPORTS_AGPS && SUPPORTS_GPS_TEST_MODUS)
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsTestMode"), &dwTestModus);

    // If test mode is activated, start with powered down GPS engine.
    if (dwTestModus) {
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"0\"\r", CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
        memset(&m_tLastDownload, 0, sizeof(FILETIME));    // Delete last download date since file has now been deleted from module.
        m_dwEngineStatus = 0;
    }

    if (SUPPORTS_AGPS) {
        char szAGPSFile[MAX_PATH];
        INT nStartMode = AGPS_STARTMODE_NO_DATA;     // Default: start with local data.
        DWORD dwAGPSFileSize = 0;
        DWORD dwRetriesStartCommand = 10;
        DWORD dwRetryDelayStartCommand = 200;

        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsStartCmdRetries"), &dwRetriesStartCommand);
        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsStartCmdRetryDelay"), &dwRetryDelayStartCommand);

        // While in test mode delete the current A-GPS file before downloading the new one.
        if (dwTestModus)
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SBNW=agps,-1\r", CMDOPT_NONE, APIID_DELETE_AGPS, 5000, 0, 0);
        // If necessary, download AGPS file now.
        if (NeedAGPSDownload()) {
            DWORD dwDelay = AGPS_DEFAULT_DOWNLOAD_DELAY;

            if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSDownloadDelay"), &dwDelay))
                dwDelay = AGPS_DEFAULT_DOWNLOAD_DELAY;

            if (GetAGPSFile(szAGPSFile, &dwAGPSFileSize)) {
                // If an AGPS file is available, upload it to the module.
                StartWaitForAGPSUpload();
                if (INCLUDE_AGPS_LENGTH_IN_FILE)
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SBNW=agps,0,1\r");
                else
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SBNW=agps,%d\r",  dwAGPSFileSize);
                if (QueueInternalMultipartCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, szAGPSFile, CMDOPT_INTERMEDIATERESPONSE, APIID_UPLOAD_AGPS, 0, 0, 0, NULL, NULL, (LPVOID)dwAGPSFileSize)) {
                    // Download completed, store date of this successful download.
                    GetCurrentFT(&m_tLastDownload);
                    // Give module a moment to accept download.
                    Sleep(dwDelay);
                    // Double check, if this file was really accepted by the radio.
                    if (!NeedAGPSDownload())
                        nStartMode = AGPS_STARTMODE_WITH_DATA;
                }
                (void)RemoveAGPSFile();
            }
        }
        else {
            // There is already a valid A-GPS file in the module.
            nStartMode = AGPS_STARTMODE_WITH_DATA;
        }

        // Activate GPS engine.
        if (NEW_AGPS_CONFIG_OPTIONS) // New configuration options introduced with PH8 Rel2.
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Engine\",%d\r",  nStartMode == AGPS_STARTMODE_WITH_DATA ? 2 : 1);
        else         // Old configuration option used in PH8 Rel1.
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"StartMode\",%d\r",  nStartMode);
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, dwRetriesStartCommand, dwRetryDelayStartCommand);
        m_dwEngineStatus = 1;

        // If an A-GPS file is available, the ODM/ODR power mode can be enabled (if supported
        // by the module). This feature is introduced with PH8 Rel2.
        if (SUPPORT_GPS_POWERSAVER && NEW_AGPS_CONFIG_OPTIONS && nStartMode == AGPS_STARTMODE_WITH_DATA) {
            DWORD dwPowerSaverODM = 0;

            if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsPowerSaverODM"), &dwPowerSaverODM)) {
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Power/Odm\",%d\r",  dwPowerSaverODM);
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
            }
        }
    }
}

HRESULT ParseGetCurrentAPN(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/){
    char szTemp[MAX_ATCMD_LEN];

    pBlob = NULL;
    cbBlob = 0;

    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CGDCONT: 1,\"IP\"", szRsp)) {
        goto Error;
    }
    if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
        !ParseString(szRsp, szTemp, MAX_ATCMD_LEN, szRsp)) {
        goto Error;
    }

    (void)_snprintfz(gCurrentAPN, MAX_ATCMD_LEN, "%s", szTemp);
    fToSetAGPS = TRUE;
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: APN got form moudle is %s\r\n"), gCurrentAPN));

Error:
    // We don't care about errors, if APN get failed, GPS engine mode will be fallback.
    return S_OK;

}

HRESULT ParseGetCurrentAssistDate(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/){
    UINT assistData = 0;

    pBlob = NULL;
    cbBlob = 0;

    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SGPSC: \"Assist/Data\"", szRsp)) {
        goto Error;
    }
    if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
        !ParseUInt(szRsp, TRUE, assistData, szRsp)){
        goto Error;
    }

    //No need to setup AGPS if Ephemeris & Almanac data available.
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Assist Data got form moudle is %d\r\n"), assistData));
    if(assistData < 6){
        fToSetAGPS = TRUE;
    }
    
Error:
    // We don't care about errors as we have default value.
    return S_OK;

}


//////////////////////////////////////////////////////////////////////////////
//
// For modules with internal A-GPS support only: Setup PDP + SUPL connection and start GPS engine.
//
// Params: (none)
//
// Return: (none)
//
//////////////////////////////////////////////////////////////////////////////

void CGpsHandling::PerformInternalAGPSStart(){
    char  szCmd[MAX_ATCMD_LEN];
    DWORD dwNMEAFreq = 0;
    DWORD dwEngineMod = 0;
    WCHAR wszAPNReg[MAX_ATCMD_LEN];
    BOOL fRet = FALSE;
    
    if(!SUPPORTS_INTERNAL_AGPS) // For module which don't support internal AGPS, stop here.
        return;

    /*if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NmeaFrequency"), &dwNMEAFreq)){
        (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"NMEA/Freq\", %d\r", dwNMEAFreq);
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD,m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
    }*/

    //Set here to avoid error.
    if(!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Nmea/Interface\",\"usb5\"\r", CMDOPT_NONE, APIID_NONE, 5000, 0, 0)){
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: GPS config Nmea/Interface, Failure!\r\n")));
        return;
    }

    if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GPSEngineMode"), &dwEngineMod)){
        if(ENGINEMODE_STANDALONE== dwEngineMod)
        {
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Read reg EngineMod as Standalone mode\r\n")));
            goto FALLBACK;
        }else{
            WCHAR serverType[MAX_ATCMD_LEN];
            WCHAR serverAddr[MAX_ATCMD_LEN];
            WCHAR serverPort[MAX_ATCMD_LEN];

            fToSetAGPS = FALSE;
            //0). Check whether the Assistdata existed, if so, just fallback.
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Assist/Data\",\"7\",\"1\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, ParseGetCurrentAssistDate);
            if(!fToSetAGPS){
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Assist data available, Fallback to standalone mode\r\n")));
                goto FALLBACK;
            }
            
            //1). Read the APN from reg.key
            if(GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSAPNSetting"), wszAPNReg, MAX_ATCMD_LEN)){
                (void)_snprintfz(gCurrentAPN, MAX_ATCMD_LEN, "%S", wszAPNReg);
                fToSetAGPS = TRUE;

            }else{ 
                //2). If 1) failed, read the APN from module.
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT+CGDCONT?\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, ParseGetCurrentAPN);
            }
            
            //3). If 0) Assist data valid, or 1) & 2) failed, start engine by standalone mode.
            if(!fToSetAGPS){
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Failed to get APN on AGPS mode, Fallback to standalone mode\r\n")));
                goto FALLBACK;
            }
			
            //4). If 1) or 2) success, setup server address & PS connection.
            //4-1) Set AGPS server address.
            memset(serverType, 0, sizeof(serverType));
			memset(serverAddr, 0, sizeof(serverAddr));
			memset(serverPort, 0, sizeof(serverPort));
            if(GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSServerType"), serverType, MAX_ATCMD_LEN) && 
            	GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSServerHost"), serverAddr, MAX_ATCMD_LEN) &&
            	GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AGPSServerPort"), serverPort, MAX_ATCMD_LEN)){

				(void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Assist/Address\",\"%S\",\"%S\",\"%S\"\r", serverType, serverAddr, serverPort);
            	(void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
            }else{
				RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Failed to get Internal AGPS server address, Fallback to sandalone mode\r\n")));
				goto FALLBACK;
			}
			
            //4-2) Set PS & SUPL connection
            if(SetupAssistGPSConnection()){
                //IF SUPL connection setup success, start GPS engine by assist mode.
                (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Engine\",%d\r", dwEngineMod);
                if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 90*1000, 0, 0)) {//Wait for 1.5 minutes.
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: AGPS mode start failed, Fallback to standalone mode\r\n")));
                    //Release current assisted gps connection.
                    ReleaseAssistGPSConnection();
                    goto FALLBACK;
                }else{
                    fToSetAGPS = FALSE;
                    m_dwEngineStatus = 1;
                }    
            }else{
                //Deactive the PS context and fallback to standalone mode.
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Assist connection setup failed, Fallback to standalone mode\r\n")));
                (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT+CGACT=0,%d\r", INTERNAL_AGPS_PDP_CONTEXT);
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 0, 0);
                goto FALLBACK;
            }
        }
        
    }

    //If GPS engine mode can't be read, just return.
    return;
    
FALLBACK:
    (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Engine\", %d\r", ENGINEMODE_STANDALONE);
    fToSetAGPS = FALSE;
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 2, 1000)) {
       RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Failed to get APN on AGPS mode, Fallback to standalone mode\r\n")));
       return;
    }
    m_dwEngineStatus = 1;
    return;
}

//////////////////////////////////////////////////////////////////////////////
//
// Sets PS and SUPL connection.
//
// Params: (none)
//
// Return: (none)
//
//////////////////////////////////////////////////////////////////////////////

BOOL CGpsHandling::SetupAssistGPSConnection(){
    char  szCmd[MAX_ATCMD_LEN];
    
    if(!SUPPORTS_INTERNAL_AGPS)
        return FALSE;

    //1) Set new PDP context.
    (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT+CGDCONT=%d,\"IP\",\"%s\"\r", INTERNAL_AGPS_PDP_CONTEXT, gCurrentAPN);
    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);

    //2) Set SGPSDNS
    (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT^SGPSDNS=%d,1\r", INTERNAL_AGPS_PDP_CONTEXT);
    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);

    //3) Active the PDP context.
    (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT+CGACT=1,%d\r", INTERNAL_AGPS_PDP_CONTEXT);
    if(QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 0, 0)){

        //4) Setup SUPL connection based on the actived PDP context.
        //Only need to check the result of this command.
        (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Assist/Connection\",%d,0\r", INTERNAL_AGPS_PDP_CONTEXT);
        if(QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 0, 0)){

            m_fAssistGPSConnection = TRUE;
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Setup AssistGPS Connection success!\r\n")));
            return TRUE;
        }
    }
    
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Delete the SUPL and PS connection.
//
// Params: (none)
//
// Return: (none)
//
//////////////////////////////////////////////////////////////////////////////

void CGpsHandling::ReleaseAssistGPSConnection(){
    char szCmd[MAX_ATCMD_LEN];
    
    if(!SUPPORTS_INTERNAL_AGPS)
        return;

    if(m_fAssistGPSConnection){
        //Release SUPL connection.
        (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Assist/Connection\",%d,1\r", INTERNAL_AGPS_PDP_CONTEXT);
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 0, 0);

        //Release PS connection.
        (void)_snprintf(szCmd, MAX_ATCMD_LEN, "AT+CGACT=0,%d\r", INTERNAL_AGPS_PDP_CONTEXT);
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 0, 0);
    }

    m_fAssistGPSConnection = FALSE;
}


//////////////////////////////////////////////////////////////////////////////
//
// Sets come advanced NMEA configurations like output frequency or features
// dead reckoning or Glonass.
//
// Params: (none)
//
// Return: (none)
//
//////////////////////////////////////////////////////////////////////////////
void CGpsHandling::AdvancedGPSConfiguration() const {
    DWORD dwReg = 0;
    char szCmd[MAX_ATCMD_LEN];

    // Adjust the NMEA output frequency, if configured.
    ApplyNmeaFrequencySetting();

    // Switch the Dead Reckoning feature, if supported.
    if (SUPPORTS_DEAD_RECKONING) {
        // Check if configuration is available, otherwise keep module default.
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsDeadReckoning"), &dwReg)) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/DeadReckoning\",\"%s\"\r",  dwReg==1?"on":"off");
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Send the AT commands to the module to enable or disable GPS mode.
// This function must  block until all necessary operations are finished.
// The GPS functionality is currently hardcoded for operation on mux
// channel 3. The ports must be configured correspondingly in the registry.
//
// Params:
//  fEnable: Flag indicating if GPS mode shall be switched on (TRUE)
//           or off (FALSE).
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::SwitchGpsState(BOOL fEnable) {
  DWORD dwGpsCmdParam = GPS_ANTENNA_POWER_SUPPL_EXTERNAL; // GPS without module gps antenna power supply
  bool  fModuleAntPowerSupply = FALSE;
  char  szCmd[MAX_ATCMD_LEN];

  SYNCBLOCK(m_csLock);

  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: SwitchGpsState(%s)\r\n"), fEnable ? TEXT("on") : TEXT("off")));

  if (!m_pRilDevice) return FALSE;
  if (!m_hCloseEvent) return FALSE;
  if (!m_hLockEvent) return FALSE;
  if (!SUPPORTS_GPS) return FALSE;

  if (fEnable == IsGpsActive()) {
    // Nothing to do
    return TRUE;
  }

  if (fEnable) {
    DWORD dwPowerSaver = 0;

    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Switch GpsState GPSInternalPort is %s \r\n"), g_ComManager.GetInternalGpsPortName()));

    // Open the underlaying multiplex port
    m_hPort = CreateFile(g_ComManager.GetInternalGpsPortName(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (m_hPort == INVALID_HANDLE_VALUE){
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Switch GpsState GetInternalGpsPort Failed! \r\n")));
        return FALSE;
    }
    // Make advanced configuration, as far it is supported by the radio.
    AdvancedGPSConfiguration();

    // Perfrom A-GPS download (if possible).
    LoadAGPSParameters();

    // Start Internal AGPS engine mode.
    PerformInternalAGPSStart();
    
    // module gps antenna power supply?
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsAntVoltSuppl"), &fModuleAntPowerSupply) && fModuleAntPowerSupply) {
      // Send the AT command to switch ON GPS Antenna Voltage Supply
      dwGpsCmdParam = GPS_ANTENNA_POWER_SUPPL_MODULE; // module gps antenna power supply
    } else {
      // external gps antenna power supply
      (void)RHA_GpsAntennaPowerSupplyOn();
    }

    // Generate notification about GPS activity
    (void)m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_GPS_ACTIVITY, GPS_ACTIVITY_START_APPLICATION);

    // Send the AT command to enable GPS operation
    if (SUPPORTS_NMEA_BUFFER_MODUS) {
      if (m_dwEngineStatus == 0) {    // The GPS Engine might be off, because of device suspend. In this case it must be turned on now.
          if(SUPPORTS_INTERNAL_AGPS) {
            //On IMC platform, Engine has differnet start mode.
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"3\"\r", CMDOPT_NONE, APIID_NONE, 10000, 2, 1000);
          }else{
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"1\"\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000);
          }
	   }
       (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Output\",\"%s\"\r",  GetSGPSCfromSGPSS(dwGpsCmdParam));
    }
    else {
      (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSS=%d\r",  dwGpsCmdParam);
    }

    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 10000, 2, 1000)) {
       ForceDeactivate();
       return FALSE;
    }

    if (SUPPORT_GPS_POWERSAVER && !NEW_AGPS_CONFIG_OPTIONS && GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsPowerSaver"), &dwPowerSaver)) {
      // Send the AT command to configure power saver for GPS (PH8 Rel1 only).
      (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"PowerSaver\",%d\r",  dwPowerSaver);
      (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
    }

    ResetEvent(m_hCloseEvent);
    SetEvent(m_hLockEvent);
  } else {
    // If GPS handling is already locked we simply have to set the close event
    if (IsGpsLocked()) {
      SetEvent(m_hCloseEvent);
      return TRUE;
    }

    SetEvent(m_hCloseEvent);
    ResetEvent(m_hLockEvent);

    // Close the underlaying multiplex port
    CloseHandle(m_hPort);
    m_hPort = INVALID_HANDLE_VALUE;

    // module gps antenna power supply?
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsAntVoltSuppl"), &fModuleAntPowerSupply) && fModuleAntPowerSupply) {
      // Send the AT command to switch ON GPS Antenna Voltage Supply
      dwGpsCmdParam = GPS_ANTENNA_POWER_SUPPL_MODULE; // module gps antenna power supply
    }

    if (TRUE == m_fGpsPassive) {
      // disable only NMEA data
      dwGpsCmdParam--; // AT^SGPSS=1 or AT^SGPSS=3

      if (SUPPORTS_NMEA_BUFFER_MODUS) {
        if (m_dwEngineStatus == 0) {    // The GPS Engine might be off, because of device suspend. In this case it must be turned on now.
            if(SUPPORTS_INTERNAL_AGPS){
                //On IMC platform, Engine has differnet start mode.
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"3\"\r", CMDOPT_NONE, APIID_NONE, 10000, 2, 1000);
            }else{
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"1\"\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000);
            }
		 }
         (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Output\",\"%s\"\r",  GetSGPSCfromSGPSS(dwGpsCmdParam));
      }
      else {
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSS=%d\r", dwGpsCmdParam);
      }
      if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 2, 1000)) {
        ForceDeactivate();
        return FALSE;
      }

    } else {

      if (dwGpsCmdParam == GPS_ANTENNA_POWER_SUPPL_EXTERNAL) {
        (void)RHA_GpsAntennaPowerSupplyOff();
      }

      // Generate notification about GPS activity
      (void)m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_GPS_ACTIVITY, GPS_ACTIVITY_STOP);

      m_dwEngineStatus = 0;
      // Send the AT command to disable GPS operation
      ReleaseAssistGPSConnection();
      if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, SUPPORTS_NMEA_BUFFER_MODUS ? "AT^SGPSC=\"Engine\",\"0\"\r" : "AT^SGPSS=0\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000)) {
        ForceDeactivate();
        return FALSE;
      }
    }
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Enable or disable GPS the same way like SwitchGpsState() but only block
// further external port access instead of denying it.
//
// Params:
//  fLock: Flag indicating if GPS mode shall be locked (TRUE) or
//         unlocked (FALSE).
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::LockGpsAccess(BOOL fLock) {
  DWORD     dwGpsCmdParam = GPS_ANTENNA_POWER_SUPPL_EXTERNAL; // GPS without module gps antenna power supply
  bool      fModuleAntPowerSupply = FALSE;
  DWORD     dwKeepGpsActive = 0;
  DWORD     dwGpsBufferMode = 0;
  char      szCmd[MAX_ATCMD_LEN];

  SYNCBLOCK(m_csLock);

  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: LockGpsAccess(%s)\r\n"), fLock ? TEXT("lock") : TEXT("unlock")));

  if (!m_pRilDevice) return FALSE;
  if (!m_hCloseEvent) return FALSE;
  if (!m_hLockEvent) return FALSE;
  if (!SUPPORTS_GPS) return FALSE;

  if (!IsGpsActive()) {
    // GPS locking makes only sense if GPS is active
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: LockGpsAccess(%s) Gps not active\r\n"), fLock ? TEXT("lock") : TEXT("unlock")));
    return FALSE;
  }

  if (IsGpsLocked() == fLock) {
    // Nothing to do
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: LockGpsAccess(%s) Gps is locked\r\n"), fLock ? TEXT("lock") : TEXT("unlock")));
    return TRUE;
  }

  GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("KeepGpsActiveOnSuspend"), &dwKeepGpsActive);
  GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsBufferMode"), &dwGpsBufferMode);

  if (!fLock) {

    // Open the underlaying multiplex port
    m_hPort = CreateFile(g_ComManager.GetInternalGpsPortName(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (m_hPort == INVALID_HANDLE_VALUE) {

        return FALSE;
    }

    // module gps antenna power supply?
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsAntVoltSuppl"), &fModuleAntPowerSupply) && fModuleAntPowerSupply) {
      // Send the AT command to switch ON GPS Antenna Voltage Supply
      dwGpsCmdParam = GPS_ANTENNA_POWER_SUPPL_MODULE; // module gps antenna power supply
    } else {
      // external gps antenna power supply
      (void)RHA_GpsAntennaPowerSupplyOn();
    }

    // Generate notification about GPS activity
    (void)m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_GPS_ACTIVITY, GPS_ACTIVITY_START_APPLICATION);

    // Send the AT command to enable GPS operation
    if (SUPPORTS_NMEA_BUFFER_MODUS) {
      if (m_dwEngineStatus == 0) {   // The GPS Engine might be off, because of device suspend. In this case it must be turned on now.
        if(SUPPORTS_INTERNAL_AGPS){
            //On IMC platform, Engine has differnet start mode.
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"3\"\r", CMDOPT_NONE, APIID_NONE, 10000, 2, 1000);
        }else{
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"1\"\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000);
        }
	  }
      (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Output\",\"%s\"\r",  GetSGPSCfromSGPSS(dwGpsCmdParam));
    }
    else {
      (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSS=%d\r", dwGpsCmdParam);
    }
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 2, 1000)) {
      ForceDeactivate();
      return FALSE;
    }

    SetEvent(m_hLockEvent);
  } else {
    ResetEvent(m_hLockEvent);

    // Close the underlaying multiplex port
    CloseHandle(m_hPort);
    m_hPort = INVALID_HANDLE_VALUE;

    // module gps antenna power supply?
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsAntVoltSuppl"), &fModuleAntPowerSupply) && fModuleAntPowerSupply) {
      // Send the AT command to switch ON GPS Antenna Voltage Supply
      dwGpsCmdParam = GPS_ANTENNA_POWER_SUPPL_MODULE; // module gps antenna power supply
    }

    if (TRUE == m_fGpsPassive) {
      // disable only NMEA data
      dwGpsCmdParam--; // AT^SGPSS=1 or AT^SGPSS=3

      if (SUPPORTS_NMEA_BUFFER_MODUS) {
        if (m_dwEngineStatus == 0) {   // The GPS Engine might be off, because of device suspend. In this case it must be turned on now.
          if(SUPPORTS_INTERNAL_AGPS){
            //On IMC platform, Engine has differnet start mode.
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"3\"\r", CMDOPT_NONE, APIID_NONE, 10000, 2, 1000);
          }else{
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"1\"\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000);
          }
		}
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Output\",\"%s\"\r",  GetSGPSCfromSGPSS(dwGpsCmdParam));
      }
      else {
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSS=%d\r", dwGpsCmdParam);
      }
      if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 2, 1000)) {
        ForceDeactivate();
        return FALSE;
      }

    } else {
      if (dwGpsCmdParam == GPS_ANTENNA_POWER_SUPPL_EXTERNAL && dwKeepGpsActive == 0) {
        (void)RHA_GpsAntennaPowerSupplyOff();
      }

      // Generate notification about GPS activity
      (void)m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_GPS_ACTIVITY, GPS_ACTIVITY_STOP);

      // Send the AT command to disable GPS operation
      if (SUPPORTS_NMEA_BUFFER_MODUS && dwKeepGpsActive && dwGpsBufferMode) {
        // If configured (and possible), put module in the NMEA buffer mode.
        QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Nmea/Output\",\"buffered\"\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000);
      }
      else {
        if (SUPPORTS_NMEA_BUFFER_MODUS) {
          if (dwKeepGpsActive) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Output\",\"%s\"\r",  GetSGPSCfromSGPSS(dwGpsCmdParam - 1));
          }
          else {
            m_dwEngineStatus = 0;
            //Need to release PS & SUPL connection in case of started by AGPS mode.
            ReleaseAssistGPSConnection();
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Engine\",\"0\"\r");
          }
        }
        else {
          (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSS=%d\r", dwKeepGpsActive ? dwGpsCmdParam - 1 : 0);
        }
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 2, 1000)) {
          ForceDeactivate();
          return FALSE;
        }
      }
    }
  }
  return TRUE;
}


//////////////////////////////////////////////////////////////////////////////
//
// Enable or disable GPS only for emergency call
//
// Params:
//  fLock: Flag indicating if GPS mode shall be locked (TRUE) or
//         unlocked (FALSE).
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::SwitchGpsStatePassive(BOOL fEnable) {
  DWORD  dwGpsCmdParam = GPS_PASSIVE_ANTENNA_POWER_SUPPL_EXTERNAL; // GPS without module gps antenna power supply
  bool   fModuleAntPowerSupply = FALSE;
  char   szCmd[MAX_ATCMD_LEN];

  SYNCBLOCK(m_csLock);

  if (!m_pRilDevice) return FALSE;
  if (!SUPPORTS_GPS) return FALSE;

  RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: SwitchGpsStatePassive(%s)\r\n"), fEnable ? TEXT("on") : TEXT("off")));

  if (fEnable ==  m_fGpsPassive)
  {
    // Nothing to do
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: SwitchGpsStatePassive() Gps is passive %s\r\n"), fEnable ? TEXT("on") : TEXT("off")));
    return TRUE;
  }

  if (fEnable) {
    DWORD dwReg = 0;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ConfigureGpsEngineOnECall"), &dwReg);
    m_fConfigureGpsOnPassiveMode = (dwReg == 1 ? TRUE : FALSE);

    if (!IsGpsActive()) {
      if (m_fConfigureGpsOnPassiveMode) {
        // Make advanced configuration, as far it is supported by the radio.
        AdvancedGPSConfiguration();

        // Perfrom A-GPS download (if possible).
        LoadAGPSParameters();

        // Start Internal GPS engine mode.
        PerformInternalAGPSStart();
      }

      if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsAntVoltSuppl"), &fModuleAntPowerSupply) && fModuleAntPowerSupply) {
        // Send the AT command to switch ON GPS Antenna Voltage Supply
        dwGpsCmdParam = GPS_PASSIVE_ANTENNA_POWER_SUPPL_MODULE; // module gps antenna power supply
      } else {
        // external gps antenna power supply
        (void)RHA_GpsAntennaPowerSupplyOn();
      }

      // Generate notification about GPS activity
      (void)m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_GPS_ACTIVITY, GPS_ACTIVITY_START_EMERGENCY);

      if (m_fConfigureGpsOnPassiveMode) {
        // Send the AT command to enable GPS operation
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: SwitchGpsStatePassive(%s) -> send AT-Command\r\n"), fEnable ? TEXT("enable") : TEXT("disable")));
        if (SUPPORTS_NMEA_BUFFER_MODUS) {
          if (m_dwEngineStatus == 0) {   // The GPS Engine might be off, because of device suspend. In this case it must be turned on now.
              if(SUPPORTS_INTERNAL_AGPS){
                //On IMC platform, Engine has differnet start mode.
                  (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"3\"\r", CMDOPT_NONE, APIID_NONE, 10000, 2, 1000);
              }else{
                  (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, "AT^SGPSC=\"Engine\",\"1\"\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000);
              }
		   }
           (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Output\",\"%s\"\r",  GetSGPSCfromSGPSS(dwGpsCmdParam));
        }
        else {
          (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSS=%d\r", dwGpsCmdParam);
        }
        // Higher command priority to avoid, that other commands (especially ATD)
        // are overtaking the GPS command in the command queue.
        if (!QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_URC, APIID_NONE, 5000, 2, 1000)) {
          return FALSE;
        }
      }
    }
    m_fGpsPassive = TRUE;
  } else {

    if (!IsGpsActive()) {
      // module gps antenna power supply?
      if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsAntVoltSuppl"), &fModuleAntPowerSupply) && fModuleAntPowerSupply) {
        // Send the AT command to switch ON GPS Antenna Voltage Supply
        dwGpsCmdParam = GPS_PASSIVE_ANTENNA_POWER_SUPPL_MODULE; // module gps antenna power supply
      }

      if (dwGpsCmdParam == GPS_PASSIVE_ANTENNA_POWER_SUPPL_EXTERNAL) {
        (void)RHA_GpsAntennaPowerSupplyOff();
      }

      // Generate notification about GPS activity
      (void)m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_GPS_ACTIVITY, GPS_ACTIVITY_STOP);

      if (m_fConfigureGpsOnPassiveMode) {
        //Need to release PS & SUPL connection in case of started by AGPS mode.
        ReleaseAssistGPSConnection();
          
        // Send the AT command to disable GPS operation
        if (!QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, SUPPORTS_NMEA_BUFFER_MODUS ? "AT^SGPSC=\"Engine\",\"0\"\r" : "AT^SGPSS=0\r", CMDOPT_NONE, APIID_NONE, 5000, 2, 1000)) {
          return FALSE;
        }
      }
    }
    m_fGpsPassive = FALSE;
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Map ReadFile() calls to the underlaying port.
//
// Params:
//  pBuf : The buffer receiving the read data.
//  dwLen: Length of the buffer.
//
// Return:
//  The number of bytes read, -1 in case of an error.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CGpsHandling::Read(LPVOID pBuf, DWORD dwLen) {
HANDLE  hEvents[] = { m_hLockEvent, m_hCloseEvent };
DWORD   dwRead;

  while (WaitForMultipleObjects(2, hEvents, FALSE, INFINITE) == WAIT_OBJECT_0) {
    if (ReadFile(m_hPort, pBuf, dwLen, &dwRead, NULL))
        {
            RILRetailTraceZone(ZONE_INFO, (TEXT("Read NMEA Lenth=(%d) \r\n"), dwRead));
            if (dwRead > 0 && dwRead < NMEA_TRACE_BUFFER_SIZE && (g_dwDebugZones&ZONE_NMEA) != 0) {
                char szNMEA[NMEA_TRACE_BUFFER_SIZE];

                memset(szNMEA, 0, NMEA_TRACE_BUFFER_SIZE);
                memcpy(szNMEA, pBuf, dwRead);
                ExtRILRetailTraceZone(ZONE_NMEA, TEXT("Receive NMEA (%s): "), L"GPS port", szNMEA);
            }
            return dwRead;
    }
    if (!IsGpsActive() || !IsGpsLocked()) return (DWORD)-1;
  }
  return (DWORD)-1;
}

//////////////////////////////////////////////////////////////////////////////
//
// Map WriteFile() calls to the underlaying port.
//
// Params:
//  pBuf : The buffer with the date to be written.
//  dwLen: Length of the buffer.
//
// Return:
//  The number of bytes written, -1 in case of an error.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CGpsHandling::Write(LPCVOID pBuf, DWORD dwLen) {
HANDLE  hEvents[] = { m_hLockEvent, m_hCloseEvent };
DWORD   dwWritten;

  while (WaitForMultipleObjects(2, hEvents, FALSE, INFINITE) == WAIT_OBJECT_0) {
    if (WriteFile(m_hPort, pBuf, dwLen, &dwWritten, NULL)) return dwWritten;
    if (!IsGpsActive() || !IsGpsLocked()) return (DWORD)-1;
  }
  return (DWORD)-1;
}

//////////////////////////////////////////////////////////////////////////////
//
// Map DeviceIoControl() calls to the underlaying port.
//
// Params:
//  dwCode   : Specifies a value indicating the I/O control operation to
//             perform. These codes are device-specific and are usually
//             exposed to programmers by means of a header file.
//  pBufIn   : Pointer to the buffer containing data to be transferred to
//             the device.
//  dwLenIn  : Specifies the number of bytes of data in the buffer specified
//             for pBufIn.
//  pBufOut  : Pointer to the buffer used to transfer the output data from
//             the device.
//  dwLenOut : Specifies the maximum number of bytes in the buffer specified
//             by pBufOut.
//  pdwActOut: Pointer to the DWORD buffer that this function uses to return
//             the actual number of bytes received from the device.
//
// Return:
//  TRUE on successs, FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::IoControl(DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActOut) {
HANDLE hEvents[] = { m_hLockEvent, m_hCloseEvent };

  while (WaitForMultipleObjects(2, hEvents, FALSE, INFINITE) == WAIT_OBJECT_0) {
    if (DeviceIoControl(m_hPort, dwCode, pBufIn, dwLenIn, pBufOut, dwLenOut, pdwActOut, NULL)) return TRUE;
    if (!IsGpsActive() || !IsGpsLocked()) return FALSE;
  }
  return FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// This function applies the NMEA frequency setting (if the module supports
// this feature). This function doesn't do any error checking, so there is not
// return value.
//
//////////////////////////////////////////////////////////////////////////////
void CGpsHandling::ApplyNmeaFrequencySetting() const {
DWORD dwNmeaFrequency = 1;

  // Not all modules support this feature.
  if (!NEW_AGPS_CONFIG_OPTIONS && !SUPPORTS_INTERNAL_AGPS)
    return;

  // Read new content of key and apply it to the radio.
  if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsNmeaFrequency"), &dwNmeaFrequency)) {
    char szCmd[MAX_ATCMD_LEN];

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSC=\"Nmea/Freq\",%d\r",  dwNmeaFrequency);
    // Don't evaluate error code. The only reason this command to fail is, that the module
    // is off. In that case GPS isn't active anyhow and the command will be repeated as
    // soon GPS gets turned on again.
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Apply Nmea Frequency as %d\r\n"), dwNmeaFrequency));
    (void)QueueInternalCmd(HCCMD_STANDARD, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_NONE, 5000, 0, 0);
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread for registry changes, see NmeaRegChangeHandler() for actual
// code to handle registry changes.
//
// Params:
//   lpParameter: handle to CGpsHandling object
//
// Return:
// Function should never return (gets terminated on destruction)
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CGpsHandling::NmeaRegChangeThread(LPVOID lpParameter) {
CGpsHandling* pHandle = (CGpsHandling *)lpParameter;

  if (!pHandle) {
    return (DWORD)-3;
  }

  return pHandle->NmeaRegChangeHandler();
}

#ifdef RILDRV_CE6
// The Prefast tool complains about missing parameters in CeFindFirstRegChange(),
// which doesn't make sense, because those parameters are optional. Therefore
// this warning is disabled here.
#pragma warning (push)
#pragma warning ( disable : 6309 6387 )
#else
#pragma prefast(push)
#pragma prefast(disable: 309, "Warnings about the CeFindFirstRegChange functions don't apply here")
#endif
//////////////////////////////////////////////////////////////////////////////
//
// Thread for detection od registry changes. The requirement ist, that the RIL
// should apply changes to the "GpsNmeaFrequency" registry key immediately.
// This requires a thread which hook into the the registry change notifications
// of Windows.
//
// Params: none
//
// Return:
// Function should never return (gets terminated on destruction)
//
//////////////////////////////////////////////////////////////////////////////
DWORD CGpsHandling::NmeaRegChangeHandler() {
HKEY hRegKey = 0;
HANDLE hEvent = INVALID_HANDLE_VALUE;
REG_NOTIFY_INFORMATION *regInfo = NULL;
REG_NOTIFY_INFORMATION *regInfoBuffer = NULL;
DWORD dwBytes, dwAvail;
HANDLE rghEvents[2];

  m_fNmeaRegChangeThreadRunning = TRUE;
  // Open RIL registry key for notifications.
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, 0, KEY_NOTIFY, &hRegKey) != ERROR_SUCCESS) {
    m_fNmeaRegChangeThreadRunning = FALSE;
    return (DWORD)-1;
  }
  // Initialize registry notifications for RIL key.
  hEvent = CeFindFirstRegChange(hRegKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET|FILE_NOTIFY_CHANGE_CEGETINFO);
  if (hEvent == INVALID_HANDLE_VALUE) {
    (void)RegCloseKey(hRegKey);
    m_fNmeaRegChangeThreadRunning = FALSE;
    return (DWORD)-2;
  }
  // Registry key is no longer needed.
  (void)RegCloseKey(hRegKey);

  // Relevant events: registrytion event and close event.
  rghEvents[0] = hEvent;
  rghEvents[1] = m_hNmeaRegChangeThreadCloseEvent;

  // Endless loop: wait for any RIL registry key notifications.
  for (;;) {
     // Wait for notifications about the RIL registry key
    if (WaitForMultipleObjects(2, rghEvents, FALSE, INFINITE) != WAIT_OBJECT_0) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: abort GPS registry thread\r\n")));
      break;
    }

    // Delete any old buffer content
    dwBytes = 0;
    dwAvail = 0;
    if (regInfoBuffer != NULL) FreeBlob(regInfoBuffer);
    regInfoBuffer = NULL;

    // Get new notification. In the first step only query the size.
    (void)CeRegGetNotificationInfo(hEvent, 0, NULL, 0, NULL, &dwAvail);
    if (dwAvail > 0)  // Get buffer for notification
      regInfoBuffer = (REG_NOTIFY_INFORMATION *)AllocBlob(dwAvail);
    regInfo = regInfoBuffer;
    // Read notification, if buffer available.
    if (regInfo != NULL) CeRegGetNotificationInfo(hEvent, 0, (void*)regInfo, dwAvail, &dwBytes, &dwAvail);
    // If there are any notification data, parse them.
    if (dwBytes > 0) {
      WCHAR wszName[MAX_PATH];

      // Step through all entries of the notification.
      for(;;) {
        BYTE *pTmp;

        // Get name of changed registry key
        memset(wszName, 0, sizeof(wszName));;
        memcpy(wszName, regInfo->RegName, regInfo->RegNameLength);

        // Is the changed key the NMEA frquency key? If GPS is currently in use,
        // apply the new setting.
        if (wcscmp(wszName, TEXT("GpsNmeaFrequency")) == 0 && IsGpsActive()) {
          ApplyNmeaFrequencySetting();
        }

        // Look for next entry in notification. If there aren't any more, end
        // parsing.
        if (regInfo->NextEntryOffset == 0) break;
        pTmp = (BYTE *)regInfo;
        pTmp += regInfo->NextEntryOffset;
        regInfo = (REG_NOTIFY_INFORMATION *)pTmp;
      }
    }

    // Prepare to get the next registry change notification
    CeFindNextRegChange(hEvent);
  }

  // Thread ends, do some cleanup.
  (void)CeFindCloseRegChange(hEvent);
  if (regInfoBuffer != NULL) FreeBlob(regInfoBuffer);
  m_fNmeaRegChangeThreadRunning = FALSE;
  return 0;
}
#ifdef RILDRV_CE6
#pragma warning (pop)
#else
#pragma prefast(pop)
#endif

//////////////////////////////////////////////////////////////////////////////
//
// Checks the basic GPS configuration.
//
// Params:
//  pbNeedRestart[out]: will be set to TRUE, if a restart of the module is required.
//
// Return: TRUE on success
//
//////////////////////////////////////////////////////////////////////////////
BOOL CGpsHandling::ConfigureGpsSettings(BOOL *pbNeedRestart) const
{
    BOOL fRet = TRUE;
    DWORD dwReg = 0;
    DWORD dwTimeout;
    char szNewSetting[MAX_ATCMD_LEN];

    // Get default timeout value.
    dwTimeout = m_pRilDevice->GetPreMuxInitTimeout();

    //For IMC platform, GPS antenna mode should be configured.
    if(SUPPORTS_INTERNAL_AGPS){
        //Configure GPS Antenna mode
        DWORD dwAntMode = 0;
        if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GPSAntennaMode"), &dwAntMode)){
            if(CONFIG_GPS_ANTENNA_GPIO){
                //GPIO/GPSANT need to be configured as "std" before switch Antenna mode
                //Input by UCS2 character
                if(!m_pRilDevice->CheckSCFGSetting(DATA_PORT, "Gpio/mode/GPSANT", "\"std\"", NULL, pbNeedRestart, 0)){
                	RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: GPS config Gpio/mode/GPSANT, Failure!\r\n")));
                	return FALSE;
                }
            }
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwAntMode==1?"1":"0");
            if(!m_pRilDevice->CheckSCFGSetting(DATA_PORT, "Mode/Antenna", szNewSetting, NULL, pbNeedRestart, 1)){
                //If GPIO/GPSANT not configured as "std", the will be a failure here.
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: GPS config Mode/Antenna, Failure!\r\n")));
                return false;
            }
        }

        //Configure GPS interface
		//Should not excute here.
        //on IMC platform, NMEA output interface should be "usb5" as the GpsPortInternal is "VCU6".
        //Post to GPS engine start phase.
        /*if(!m_pRilDevice->CheckSCFGSetting(DATA_PORT, "Nmea/Interface", "\"usb5\"", NULL, NULL, 1)){
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: GPS config Nmea/Interface, Failure!\r\n")));
            return false;
        }*/
    }
    
    // Make sure, that the antenna power is controlled automatically.
    if (NEW_AGPS_CONFIG_OPTIONS || SUPPORTS_INTERNAL_AGPS) {
        // No need to check the result.
        (void)m_pRilDevice->CheckSCFGSetting(DATA_PORT, "Power/Antenna", "\"auto\"", NULL, NULL, 1);
    }

    // Configure the Glonass feature, if supported.
    if (SUPPORTS_GLONASS) {
        // Check if configuration is available, otherwise keep current module default.
        if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GpsGlonass"), &dwReg)) {
            // Check current setting.
            (void)_snprintfz(szNewSetting, sizeof(szNewSetting), "\"%s\"", dwReg==1?"on":"off");
            if (!m_pRilDevice->CheckSCFGSetting(DATA_PORT, "Nmea/Glonass", szNewSetting, NULL, pbNeedRestart, 1))
                return FALSE;
        }
    }
    return fRet;
}

