/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/ForceHomeNet.cpp $
 * $Revision: /main/ms-d/8 $
 *     $Date: 2012-10-08 16:18:21 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/ForceHomeNet.cpp$ 
 * 
 * /main/ms-d/8   2012-10-08 16:18:21   pmackent 
 * Comply to Klocwork checks.
 *
 * /main/ms-d/7   2012-08-29 14:35:52   pmackent
 * Workaround for COPS changes SCFG bug
 *
 * /main/ms-d/6   2012-08-13 11:03:51   pmackent
 * Comply to CE6 code analyzer requirements
 *
 * /main/ms-d/5   2011-09-26 13:34:33   ctna0256
 * Comply with Windows CE6 warning level 4
 *
 *
*/

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

#include "precomp.h"

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

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

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CForceHomeNet::CForceHomeNet() {
  m_hThread = NULL;
  m_hRegisterEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  InitializeCriticalSection(&m_csThreadHandle);
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CForceHomeNet::~CForceHomeNet() {
  AbortThread();
  CloseHandle(m_hRegisterEvent);
  DeleteCriticalSection(&m_csThreadHandle);
}

//////////////////////////////////////////////////////////////////////////////
//
// Force login into a specific provider home net if it is possible
//
//////////////////////////////////////////////////////////////////////////////
BOOL CForceHomeNet::ForceHomeNet(CRilHandle* pRilDevice) {
  if (!FORCE_HOMENET_POSSIBLE || !IsForceHomeNetEnabled()) return FALSE;

  // Get the provider home net id
  if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pRilDevice, "AT+CIMI\r", CMDOPT_NONE, APIID_GETUSERIDENTITY, 0, 0, 0, ParseCIMIData, NULL, this)) {
    return FALSE;
  }

  // Check if the home net corresponds to the required home net
  if (m_dwRequiredHomeNet != m_dwHomeNet) {
    return FALSE;
  }

  m_pRilDevice = pRilDevice;
  SYNCBLOCK(m_csThreadHandle);
  AbortThread();
  m_hThread = CreateThread(NULL, 0, ExecutionThread, this, 0, NULL);
  if (!m_hThread) {
    return FALSE;
  }

  return TRUE;
}

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

//////////////////////////////////////////////////////////////////////////////
//
// Check if the feature is enabled
//
//////////////////////////////////////////////////////////////////////////////
BOOL CForceHomeNet::IsForceHomeNetEnabled(void) {
  if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ForceHomeNet"), &m_dwRequiredHomeNet)) {
    if (m_dwRequiredHomeNet) {
      return TRUE;
    }
  }
  return FALSE;
}


//////////////////////////////////////////////////////////////////////////////
//
// Get timeout for network reregistering
//
//////////////////////////////////////////////////////////////////////////////
DWORD CForceHomeNet::GetTimeout(void) {
DWORD dwTimeout;

  if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ForceHomeNetTimeout"), &dwTimeout)) {
    if (dwTimeout) {
      return dwTimeout;
    }
  }
  return FORCE_HOMENET_TIMEOUT;
}

//////////////////////////////////////////////////////////////////////////////
//
// Abort possibly running execution thread
//
//////////////////////////////////////////////////////////////////////////////
void CForceHomeNet::AbortThread(void) {
  SYNCBLOCK(m_csThreadHandle);
  if (m_hThread) {
#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
    TerminateThread(m_hThread, (DWORD)-1);
#ifdef RILDRV_CE6
#pragma warning (pop)
#else
#pragma prefast(pop)
#endif
    CloseHandle(m_hThread);
    m_hThread = NULL;
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned AT+CIMI data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CForceHomeNet::ParseCIMIData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CForceHomeNet*  pInstance = (CForceHomeNet*)pParam;
CHAR            strHomeNet[NETWORK_ID_LEN + 1];

  pBlob = NULL;
  cbBlob = 0;

  memset(strHomeNet, 0, sizeof(strHomeNet));
  // Parse "<prefix><IMSI><postfix>"
  if (!ParseRspPrefix(szRsp, szRsp) ||
      !ParseUnquotedString(szRsp, '\r', strHomeNet, sizeof(strHomeNet), szRsp)) {
    return E_FAIL;
  }
  pInstance->m_dwHomeNet = atoi(strHomeNet);
  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parse the returned AT+COPS=? data (callback function)
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CForceHomeNet::ParseCOPSData(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CForceHomeNet*  pInstance = (CForceHomeNet*)pParam;
CHAR            strHomeNet[NETWORK_ID_LEN + 3];

  pBlob = NULL;
  cbBlob = 0;

  // Search for the home network id
  pInstance->m_fHomeNetAvailable = FALSE;
  _snprintfz(strHomeNet, NETWORK_ID_LEN + 3, "\"%i\"", pInstance->m_dwHomeNet);
  if (strstr(szRsp, strHomeNet)) {
    pInstance->m_fHomeNetAvailable = TRUE;
  }
  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// The execution thread
//
//////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CForceHomeNet::ExecutionThread(LPVOID pParam) {
CForceHomeNet*  pInstance = (CForceHomeNet*)pParam;
DWORD           dwRet = 0;

  // Switch to 850MHz band
  if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pInstance->m_pRilDevice, "AT^SCFG=Radio/Band,64,1\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0)) {
    dwRet = (DWORD)-1;
  } else {
    // Get the operator list
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pInstance->m_pRilDevice, "AT+COPS=?\r", CMDOPT_NONE, APIID_GETOPERATORLIST, 0, 3, 1000, pInstance->ParseCOPSData, NULL, pInstance)) {
      dwRet = (DWORD)-2;
    } else {
      if (pInstance->m_fHomeNetAvailable) {
        CHAR strCmd[MAX_ATCMD_LEN];
        _snprintfz(strCmd, MAX_ATCMD_LEN, "AT+COPS=1,2,%i,2\r", pInstance->m_dwHomeNet);
        ResetEvent(pInstance->m_hRegisterEvent);
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pInstance->m_pRilDevice, strCmd, CMDOPT_NONE, APIID_REGISTERONNETWORK, 0, 0, 0)) {
          dwRet = (DWORD)-3;
        } else {
          if (WaitForSingleObject(pInstance->m_hRegisterEvent, pInstance->GetTimeout()) != WAIT_OBJECT_0) {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("Timeout waiting for registration on home net\r\n")));
            dwRet = (DWORD)-4;
          }
        }
      }
    }

    // Switch back to all bands
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pInstance->m_pRilDevice, "AT^SCFG=Radio/Band,127,1\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 3, 1000)) {
      dwRet = (DWORD)-5;
    }

    // Reenable automatic network search
    if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pInstance->m_pRilDevice, "AT+COPS=0\r", CMDOPT_NONE, APIID_REGISTERONNETWORK, 0, 3, 1000)) {
      dwRet = (DWORD)-6;
    }

    if (COPS_CHANGES_SCFG) {
        // Bug in PXS8: the use of AT+COPS changes the module from GSM-only to
        // multimode. If necessary, the RIL must change the setting back.
        if (pInstance->m_pRilDevice->GetMultiModeSetting() == MULTIMODE_CONFIG_LTE)
            (void)QueueInternalCmd(HCCMD_STANDARD, pInstance->m_pRilDevice, "AT^SCFG=MEopMode/AccT,\"GSM/UMTS\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
    }
  }

  SYNCBLOCK(pInstance->m_csThreadHandle);
  CloseHandle(pInstance->m_hThread);
  pInstance->m_hThread = NULL;
  return dwRet;
}

