/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/rilsimtk.cpp $
 * $Revision: /main/ms-d/40 $
 *     $Date: 2013-03-04 13:19:59 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/rilsimtk.cpp$ 
 * 
 * /main/ms-d/40   2013-03-04 13:19:59   pmackent 
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/39   2013-02-08 09:03:35   pmackent
 * Intel style SAT is now official
 *
 * /main/ms-d/38   2012-10-08 16:18:36   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/37   2012-09-19 15:09:44   pmackent
 * Solve problem with wrong SAT response to 27.22.4.22.1_6(IPIS100082389)
 *
 * /main/ms-d/36   2012-02-22 13:27:14   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/35   2012-02-14 15:11:45   ctna0256
 * Remove unnecessary sleep before starting SAT
 *
 * /main/ms-d/34   2012-02-10 12:59:32   ctna0256
 * Store Idle Mode Status in registry (IPIS100077395)
 *
 * /main/ms-d/33   2012-02-08 15:15:49   ctna0256
 * Add Intel-style SAT
 *
 * /main/ms-d/31   2012-02-03 14:01:01   ctna0256
 * Phonebook and SIM access now in separate files
 *
 * /main/ms-d/30   2012-02-02 17:03:57   ctna0256
 * FD phonebook workaround and NITZ URCs for Intel platform
 *
 * /main/ms-d/29   2012-01-31 16:38:58   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/28   2012-01-20 13:20:41   ctna0256
 * Better handling of SAT Idle Mode Text in case of booting the device (IPIS100077101)
 *
 * /main/ms-d/27   2011-09-26 13:35:12   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/26   2011-02-28 09:19:29   ctna0256
 * Reset SAT Idle Mode Text on start (IPIS100070301)
 *
 * /main/ms-d/25   2011-02-18 10:28:45   ctna0256
 * Merge automatic SAT response from Classic-RIL
 *
 * /main/ms-d/24   2011-02-02 10:55:20   ctna0256
 * Merge SAT call number handling from Classic-RIL
 *
 * /main/ms-d/23   2010-09-07 11:07:56   ctna0256
 * Sending AT^SSTR=254 is unnecessary on timeout (IPIS100067413)
 *
 * /main/ms-d/22   2010-09-07 10:11:48   ctna0256
 * Workaround for missing ^SSTN: 254 URC (IPIS100067413)
 *
 * /main/ms-d/21   2009-10-07 09:22:26   ctna0256
 * Introduce no-operation command queue
 * Support 2nd alpha identifier in SAT setup call (IPIS100055375)
 *
 * /main/ms-d/20   2009-07-13 09:34:52   ctna0256
 * Removed unnecessary extern declaration
 *
 * /main/ms-d/19   2009-03-12 13:29:25   ctna0256
 * New registry key to disable SAT call control envelope commands in SAT profile.
 *
 * /main/ms-d/18   2009-01-28 10:00:32   ctna0256
 * Undo last change, was intended for Classic-RIL
 *
 * /main/ms-d/16   2008-08-22 10:27:32   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/15   2008-08-21 09:35:24   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/14   2008-07-22 15:53:06   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/13   2008-04-29 13:38:10   mp49210
 * Workaround for missing simstatus URC.
 * New handling of SIM reset.
 *
 * /main/ms-d/12   2008-04-07 13:29:02   mp49210
 * Read "NoIcon" reg key at boot time
 *
 * /main/ms-d/11   2008-04-03 14:41:56   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/10   2008-03-31 13:34:44   mp49210
 * IPIS100017065: delay for automatic SAT responses
 * Support of new SSTR parameters
 * Support of 'no icon' option
 *
 * /main/ms-d/9   2008-03-14 16:09:32   mp49210
 * IPIS100045647: workaround for handling of SAT setup call while active voice call
 *
 * /main/ms-d/8   2007-12-12 16:17:39   mp49210
 * Merge SAT Setup Call handling from Classic RIL
 *
 * /main/ms-d/7   2007-11-21 16:56:13   mp49210
 * Check for missing SAT notifications in initialization.
 *
 * /main/ms-d/6   2007-08-08 11:16:52   mp49210
 * Introduction of audio profile download
 *
 * /main/ms-d/5   2007-03-27 11:35:58   mp49210
 * SAT in HC25 is now active before been activated.
 *
 * /main/ms-d/4   2007-03-01 16:29:02   mp49210
 * New AT^SSTA concept
 *
 * /main/ms-d/3   2007-02-26 16:10:59   mp49210
 * Move SAT commands to standard port
 *
 * /main/ms-d/2   2006-12-27 14:20:08   mp49210
 * Workaround for wrong CME-error at AT+CPBR
 *
 * /main/ms-d/1   2006-10-25 16:51:57   wm36264
 * First transformation from classic RIL
 *
*/

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

#ifdef RIL_ENABLE_SIMTK

#include "rilsimtk_standard.cpp"
#include "rilsimtk_intel.cpp"

//////////////////////////////////////////////////////////////////////////////
//
// Basic functions for SAT handling
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Thread to detect missing "^SSTN: 254" URC.
//
//////////////////////////////////////////////////////////////////////////////
static DWORD WINAPI SSTN254TimeoutThreadProc(LPVOID lpParameter)
{
    CSimToolkitHandling* pSATHandler = (CSimToolkitHandling*)lpParameter;

    const DWORD dwReturn = pSATHandler->SSTN254TimeoutThread();
    return dwReturn;
}

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CSimToolkitHandling::CSimToolkitHandling(CRilHandle* pRilDevice) {
  DWORD dwSATAppCanDisplayIcons = 0;
  DWORD dwAutomaticSATResponse = 0;
  DWORD dwIdleModeTextPresent = 0;

  InitializeCriticalSection(&m_csLockBuffer);
  m_pRilDevice = pRilDevice;
  m_fNoResponse = FALSE;
  m_fIgnoreNextSSTR1616 = FALSE;
  m_fIgnoreNextSSTR1632 = FALSE;
  m_fIgnoreNextSSTR40 = FALSE;
  m_fLastCommandIncludedIcon = FALSE;
  m_nLastSATDisplayTextFlags = 0;
  m_nLastSATSetCallDetail = 0;
  m_dwSATCallNameOverride = 1;
  m_fLastSetupCallNo = FALSE;
  memset(m_szLastSetupCallNo, 0, MAX_ATCMD_LEN);
  memset(m_wszLastSetupCallName, 0, UCS2_LEN*sizeof(WCHAR));
  m_hSATTimoutTerminate = CreateEvent(NULL, FALSE, FALSE, NULL);
  m_hSATTimoutAbort = CreateEvent(NULL, FALSE, FALSE, NULL);
  m_hIncoming_SSTN_URC = CreateEvent(NULL, FALSE, FALSE, NULL);
  m_hExecuted_SSTR_Cmd = CreateEvent(NULL, FALSE, FALSE, NULL);

  // Read registry key at boot time, to have a correct default value in case there
  // are SAT activities before the RIL had been initialized fully the first time.
  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATAppCanDisplayIcons"), &dwSATAppCanDisplayIcons))
    dwSATAppCanDisplayIcons = 1;
  m_fApplicationCanDisplayIcons = (dwSATAppCanDisplayIcons == 1 ? TRUE : FALSE);
  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATCallNameOverride"), &m_dwSATCallNameOverride))
    m_dwSATCallNameOverride = 1;
  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATAutomaticResponse"), &dwAutomaticSATResponse))
    dwAutomaticSATResponse = 0;
  m_fAutomaticSATResponse = (dwAutomaticSATResponse == 1 ? TRUE : FALSE);

  // Get initial idle mode text status from registry.
  if(GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, REG_LAST_IDLEMODETEXT, &dwIdleModeTextPresent))
    m_fIdleModeTextPresent = (dwIdleModeTextPresent == 1 ? TRUE : FALSE);
  else
    m_fIdleModeTextPresent = FALSE;

  // If defined via registry, start "missing SSTN254" workaround
  m_dwSSTN254Timeout = 0;
  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATSSTN254Timeout"), &m_dwSSTN254Timeout))
    m_dwSSTN254Timeout = SAT_SSTN254_DEFAULT_TIMEOUT;
  if (m_dwSSTN254Timeout > 0)
    CloseHandle(CreateThread(NULL, 0, SSTN254TimeoutThreadProc, (LPVOID)this, 0, 0));
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CSimToolkitHandling::~CSimToolkitHandling() {
  Deinit();
  SetEvent(m_hSATTimoutTerminate);
  Sleep(100);
  CloseHandle(m_hSATTimoutTerminate);
  CloseHandle(m_hSATTimoutAbort);
  CloseHandle(m_hIncoming_SSTN_URC);
  CloseHandle(m_hExecuted_SSTR_Cmd);
  DeleteCriticalSection(&m_csLockBuffer);
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize and start the SAT handling.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSimToolkitHandling::Init(CRilHandle* pRilDevice) {
  char szCmd[MAX_ATCMD_LEN];
  DWORD dwNoSTKInit = 0;
  DWORD dwSetCallControl = 0;
  DWORD dwSATAppCanDisplayIcons = 0;
  DWORD dwAutomaticSATResponse = 0;

  if (!pRilDevice) {
    return FALSE;
  }

  // Store the RIL device
  m_pRilDevice = pRilDevice;

  if (SUPPORTS_SAT_UTP_CONFIG) {
    // Configure the USIM SAT profile, if necessary.
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SetCallControl"), &dwSetCallControl);
    if (dwSetCallControl == 1)        // Set SAT profile to standard
      (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilDevice, "AT^SCFG=\"SAT/UTP\",\"FFFFFFFF7F0300DFFF0000000000000000000000\"\r", CMDOPT_NONE, APIID_FETCHSIMTOOLKITCMD, 0, 0, 0, NULL, NULL, this);
    if (dwSetCallControl == 2)        // Disable Call Control Envelope in SAT profile
      (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilDevice, "AT^SCFG=\"SAT/UTP\",\"3FE9FFFF7F03005FFF0000000000000000000000\"\r", CMDOPT_NONE, APIID_FETCHSIMTOOLKITCMD, 0, 0, 0, NULL, NULL, this);
  }

  (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyIntrinsycRIL, TEXT("NoSTKInit"), &dwNoSTKInit);

  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATCallNameOverride"), &m_dwSATCallNameOverride))
    m_dwSATCallNameOverride = 1;
  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATAppCanDisplayIcons"), &dwSATAppCanDisplayIcons))
    dwSATAppCanDisplayIcons = 1;
  m_fApplicationCanDisplayIcons = (dwSATAppCanDisplayIcons == 1 ? TRUE : FALSE);
  if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATAutomaticResponse"), &dwAutomaticSATResponse))
    dwAutomaticSATResponse = 0;
  m_fAutomaticSATResponse = (dwAutomaticSATResponse == 1 ? TRUE : FALSE);

  // Enable SAT handling.
  if (START_SAT_WITH_CFUN) {
    // For Intel style modules the activation SAT is a special CFUN mode.
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CFUN=%u\r", dwNoSTKInit==1?1:6);
    if (!QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_FETCHSIMTOOLKITCMD, 0, 2, 10000)) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SimToolkit Error - Failed to queue SAT enable cmd -> SimToolkit NOT enabled !!!\r\n")));
      return FALSE;
    }
  }
  else {
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SSTA=%u,1\r", dwNoSTKInit==1?0:1);
    if (!QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_FETCHSIMTOOLKITCMD, 0, 2, 10000)) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SimToolkit Error - Failed to queue SAT enable cmd -> SimToolkit NOT enabled !!!\r\n")));
      return FALSE;
    }
  }

  // Some SIM card send their first SAT request very early, while the USB is still
  // enumerating. For this case the RIL checks now for any pending SAT commands
  // here.
  if (!INTEL_STYLE_SAT)
      (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilDevice, "AT^SSTGI?\r", CMDOPT_NONE, APIID_FETCHSIMTOOLKITCMD, 0, 0, 0, ParseInitSATStatus, NULL, this);

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the SAT handling.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSimToolkitHandling::Deinit(void) {
  // Nothing to do for now
  m_pRilDevice = 0;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Get the SAT profile.
//
// Params:
//  pHandle: Pointer to CRilInstanceHandle to be used for the operation.
//
// Return:
//  S_OK on successs, E_FAIL otherwise.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::GetProfile(CRilInstanceHandle* pHandle) {
HRESULT hr;

  if (!QueueCmd(HCCMD_URCPORT_ONLY, pHandle, "AT^SSTA?\r", CMDOPT_NONE, APIID_GETSIMTOOLKITPROFILE, ParseGetProfile, NULL, hr)) {
    return E_FAIL;
  }
  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// Set the SAT profile.
//
// Params:
//  pHandle: Pointer to CRilInstanceHandle to be used for the operation.
//
// Return:
//  Currently only E_NOTIMPL because Cinterion modules doesn't support setting
//  of SAT profile.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::SetProfile(CRilInstanceHandle* /*pHandle*/) {
  return E_NOTIMPL;
}

//////////////////////////////////////////////////////////////////////////////
//
// Put a SAT command into the command queue.
//
// Params:
//  dwCmd: Number of SAT command.
//
// Return:
//  S_OK on successs, E_FAIL otherwise.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::QueueSimToolkitCmd(DWORD dwCmd) {
char szCmd[32];

  (void)_snprintfz(szCmd, 32, "AT^SSTGI=%d\r", dwCmd);

  if (dwCmd == SIMTK_CMD_PLAY_TONE) {
      // PLAY TONE: we are now about to receive a +CIEV: sounder and/or +CIEV: audio URC.
      // We must not ignore this URC, even when it is configured not to be used.
      m_pRilDevice->SetLastSATPlayToneCmd(GetTickCount());
  }

  if (!QueueInternalCmd(HCCMD_URCPORT_ONLY, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_FETCHSIMTOOLKITCMD, 0, SIMTK_CMD_RETRIES, SIMTK_CMD_RETRY_DELAY, ParseGetCommand, NULL, this)) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SimToolkit Error - Failed to queue SAT cmd %d\r\n"), dwCmd));
    return E_FAIL;
  }

  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parsing function called to parse a SAT notification.
//
// Params:
//  rszPointer: Pointer to the received SAT notification.
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CSimToolkitHandling::ParseNotification(LPCSTR& rszPointer) {
LPCSTR  szDummy;
UINT    nValue;

  if (!ParseUInt(rszPointer, FALSE, nValue, rszPointer)) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ParseSIMToolkitNotification: failed to find value\r\n")));
    return FALSE;
  }

  // Look for the "<postfix>" (don't increase string pointer, postfix is scanned again in global response handler)
  if (!ParseRspPostfix(rszPointer, szDummy)) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ParseSIMToolkitNotification: failed to find postfix\r\n")));
    return FALSE;
  }

  // Notify SAT timeout thread about incoming SSTN URC.
  SetEvent(m_hIncoming_SSTN_URC);

  if (nValue == 255) {
    // Full SIM reset, this corresponds to a SAT-Refresh with details 4.
    // The SIM reset impies the need for the RIL to repeat its SIM initialization.
    void*  pBlob;
    UINT   cbBlob;

    if (g_dwSIMLockedState == RIL_LOCKEDSTATE_READY) {  // Check if SIM is ready, otherwise the ^SSTN: 255 meant "SIM removed".
      CloseHandle(CreateThread(NULL, 0, SIMReInsertedThreadProc, (LPVOID)m_pRilDevice, 0, 0));
      if (WRONG_SIMSTATUS_AFTER_SAT_SIM_RESET)
        m_pRilDevice->IgnoreNextSIMStatus();
      (void)m_pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_SIMTOOLKITSESSIONEND, NULL, 0);
      m_pRilDevice->SendReadyStateNotification(RIL_READYSTATE_NONE, FALSE);
      m_pRilDevice->SendReadyStateNotification(RIL_READYSTATE_INITIALIZED, TRUE);
    }

    return ParseRefresh("4\r\n", pBlob, cbBlob);
  } else if ((nValue == 254) || ((nValue > 100) && (nValue < 200))) {
    return m_pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_SIMTOOLKITSESSIONEND, NULL, 0);
  } else {
    return SUCCEEDED(QueueSimToolkitCmd(nValue));
  }
}

//////////////////////////////////////////////////////////////////////////////
//
// Parsing callback if something went wrong -> try to bring the module back
// into a proper state.
//
// Return:
//  S_OK on successs, E_FAIL otherwise.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::ParseRspError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam, DWORD& /*dwCode*/) {
CSimToolkitHandling* pInstance = (CSimToolkitHandling*)pParam;

  RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SimToolkit: Response-Error\r\n")));

  if (!pInstance) {
    return E_FAIL;
  }

  if (!QueueInternalCmd(HCCMD_URCPORT_ONLY, pInstance->m_pRilDevice, "AT^SSTR=254\r", CMDOPT_NONE, APIID_SENDSIMTOOLKITCMDRESPONSE, 0, SIMTK_CMD_RETRIES, SIMTK_CMD_RETRY_DELAY)) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SimToolkit Error - Failed to queue quit cmd\r\n")));
    return E_FAIL;
  }

  if (!pInstance->m_pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_SIMTOOLKITSESSIONEND, NULL, 0)) {
    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: SimToolkit Error - Failed broadcast RIL_NOTIFY_SIMTOOLKITSESSIONEND\r\n")));
    return E_FAIL;
  }

  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// OK handling of AT^SSTR command: notify timeout thread
//
// Return:
//  S_OK
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::ParseRspOK(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam) {
CSimToolkitHandling* pInstance = (CSimToolkitHandling*)pParam;

  // Inform SAT timeout out thread, that an AT^SSTR command has been executed successfully.
  if (pInstance != NULL)
    pInstance->SATSSTRexecuted();

  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parsing initial SAT status.
//
// Return:
//  S_OK on successs, E_FAIL otherwise.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::ParseInitSATStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CSimToolkitHandling* pInstance = (CSimToolkitHandling*)pParam;
HRESULT  hr = S_OK;
UINT nState = 0;
UINT nCmdType = 0;

  pBlob = NULL;
  cbBlob = 0;

  // Parse "<prefix>^SSTGI: <state>,<cmdType>"
  if (!ParseRspPrefix(szRsp, szRsp)                   ||
      !MatchStringBeginning(szRsp, "^SSTGI: ", szRsp)  ||
      !ParseUInt(szRsp, TRUE, nState, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nCmdType, szRsp)  ||
      !ParseRspPostfix(szRsp, szRsp)) {
    hr = E_FAIL;
    goto Error;
  }

  if (nState == 3 && nCmdType > 0 && pInstance != NULL) {
    hr = pInstance->QueueSimToolkitCmd(nCmdType);
  }

Error:
  return hr;
}

//////////////////////////////////////////////////////////////////////////////
//
// Parsing callback to parse the the SAT profile.
//
// Return:
//  S_OK on successs, E_FAIL otherwise.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CSimToolkitHandling::ParseGetProfile(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/) {
UINT     i;
UINT     nValue;
UINT     cbProfileString;
LPSTR    szProfileString = NULL;
LPSTR    pchProfileStringWalk;
UINT     cbProfile;
BYTE*    pbProfileWalk;
BYTE*    pbProfile = NULL;
HRESULT  hr = S_OK;

  pBlob = NULL;
  cbBlob = 0;

  // Parse "<prefix>^SSTA: <state>,<alphabet>,<allowedInstance>,<Profile>"
  if (!ParseRspPrefix(szRsp, szRsp)                   ||
      !MatchStringBeginning(szRsp, "^SSTA: ", szRsp)  ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)        ||
      !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
      !MatchStringBeginning(szRsp, ",", szRsp)) {
    hr = E_FAIL;
    goto Error;
  }

  // Parse "<profile>"
  // NOTE: we take ownerswhip of allocated szProfileString
  if (!ParseUnlimitedString(szRsp, szProfileString, cbProfileString, szRsp)) {
    hr = E_FAIL;
    goto Error;
  }
  cbProfile = (cbProfileString - 1) / 2;

  // Allocate the byte array of needed size
  pbProfile = (BYTE*)AllocBlob(cbProfile);
  if (!pbProfile) {
    hr = E_OUTOFMEMORY;
    goto Error;
  }

  // Decode the profile
  pbProfileWalk = pbProfile;
  pchProfileStringWalk = szProfileString;
  for (i = 0; i < cbProfile; i++) {
    *pbProfileWalk = SemiByteCharsToByte(*pchProfileStringWalk, *(pchProfileStringWalk + 1));
    pbProfileWalk++;
    pchProfileStringWalk += 2;
  }

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

  pBlob = (void*)pbProfile;
  cbBlob = cbProfile;

Error:
  if (FAILED(hr)) {
    (void)FreeBlob(pbProfile);
  }
  delete[] szProfileString;
  return hr;
}

//////////////////////////////////////////////////////////////////////////////
//
// Thread which runs continously to detect missing "^SSTN: 254" URC.
//
//////////////////////////////////////////////////////////////////////////////
DWORD CSimToolkitHandling::SSTN254TimeoutThread(void)
{
    HANDLE  hEvents[] = { m_hSATTimoutTerminate, m_hSATTimoutAbort, m_hIncoming_SSTN_URC, m_hExecuted_SSTR_Cmd };
    DWORD dwEvent;
    DWORD dwTimeout = INFINITE;
    BOOL bRunning = TRUE;
    BOOL bWaitingForSSTN254 = FALSE;

    // Run until m_hSATTimoutTerminate event is received
    while (bRunning) {
        dwEvent = WaitForMultipleObjects(4, hEvents, FALSE, dwTimeout);
        switch (dwEvent)
        {
            case WAIT_OBJECT_0:     // m_hSATTimoutTerminate
                // RIL is about to shut down, this thread must end now.
                bRunning = FALSE;
                break;
            case WAIT_OBJECT_0 + 1:     // m_hSATTimoutAbort
                // module has been turned off, so there are no further SAT actions coming.
                bWaitingForSSTN254 = FALSE;
                dwTimeout = INFINITE;
                break;
            case WAIT_OBJECT_0 + 2:     // m_hIncoming_SSTN_URC
                // A ^SSTN URC has been received
                bWaitingForSSTN254 = FALSE;
                dwTimeout = INFINITE;
                break;
            case WAIT_OBJECT_0 + 3:     // m_hExecuted_SSTR_Cmd
                // An AT^SSTR command has been executed. Now wait for
                // for an incoming ^SSTN URC.
                bWaitingForSSTN254 = TRUE;
                dwTimeout = m_dwSSTN254Timeout;     // Timeout specified by registry.
                break;
            case WAIT_TIMEOUT:
                // Timeout: it the RIL is currently waiting for a ^SSTN-URC, then now there
                // is the situation, in which a ^SSTN: 254 URC has been missing.
                if (bWaitingForSSTN254) {
                    // Missing ^SSTN: 254 URC: send "session end" notification to SAT MMI and
                    // send an AT^SSTR=254 to the module.
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Send simulated SSTN-254-notification\r\n")));
                    (void)m_pRilDevice->BroadcastRealBlobNotification(RIL_NOTIFY_SIMTOOLKITSESSIONEND, NULL, 0);
                    bWaitingForSSTN254 = FALSE;
                }
                dwTimeout = INFINITE;
                break;
        }
    }

    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//
// Reset the Idle Mode Text (will be called on radio shutdown).
//
//////////////////////////////////////////////////////////////////////////////
void CSimToolkitHandling::ResetIdleModeText() {
    RILSIMTOOLKITCMD* pSatCmd;
    SIMTEXT* pSatText;
    CNotificationData *pnd;
    DWORD dwResetIdleModeText = 1;
    BYTE *pBuffer;

    // If no idle mode text is present, if doesn't need to be reseted.
    if (!m_fIdleModeTextPresent)
        return;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATResetIdleModeText"), &dwResetIdleModeText);
    if (dwResetIdleModeText == 0)
        return;

    pBuffer = (BYTE *)AllocBlob(SIMTK_MAX_LENGTH);
    if (pBuffer == NULL)
        return;

    (void)PrepareRilCmdStruct(pBuffer, &pSatCmd, SIM_NOTIFY_SETUPIDLEMODETEXT);
    pSatText = (SIMTEXT*)((BYTE*)pSatCmd + pSatCmd->dwDetailsOffset);
    pSatText->cbSize = ROUNDTODWORD2(sizeof(SIMTEXT), pSatText->dwTextSize);

    pSatCmd->cbSize += pSatText->cbSize;
    pSatCmd->dwDetailsSize = pSatText->cbSize;

    m_fIgnoreNextSSTR40 = TRUE;
    SetIdleModeTextPresent(FALSE);

    pnd = new CNotificationData;
    if (pnd)
    {
        if (pnd->InitFromRealBlob(RIL_NOTIFY_SIMTOOLKITEVENT, (void*)pSatCmd, pSatCmd->cbSize)) {
            m_pRilDevice->BroadcastNotification(pnd);
        }
        else
            delete pnd;
    }
    (void)FreeBlob(pBuffer);
}

//////////////////////////////////////////////////////////////////////////////
//
// Set audio mode for SAT Play Tone, if specified in registry.
//
// Return:
//  none
//
//////////////////////////////////////////////////////////////////////////////
void CSimToolkitHandling::SetSATPlayToneAudioMode() {
    DWORD dwAudioMode = 0;
    DWORD dwAudioGain = 0;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATPlayToneAudioMode"), &dwAudioMode)) {
        if (dwAudioMode < MAX_AUDIO_MODES) {
            // If currently is no call active, the RIL will set the audio mode specified
            // by the registry. If a call is active, the current audio mode will be kept.
            if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SATPlayToneMinAudioGain"), &dwAudioGain)) {
                if (dwAudioGain > g_dwCacheDownlinkVolume)
                    g_dwCacheDownlinkVolume = dwAudioGain;
            }
            if (StoreAudioSetting(dwAudioMode, TRUE)) {
                // Special problem: the RIL now has to set the audio mode in the
                // module, but since the audio URC, which would normally trigger
                // the start of the data mode, appears while the AT interpreter is
                // blocked by the SAT command, the RIL must now simulate the
                // appearance of this URC.
                m_pRilDevice->SetCIEVaudioActive(TRUE);
                RHAActivateVoicePath(m_pRilDevice);
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// To be called if the status of the SAT Idle Mode Text changes. The status
// stored in the registry to make it availabe after a reboot of the RIL.
//
// Return:
//  none
//
//////////////////////////////////////////////////////////////////////////////
void CSimToolkitHandling::SetIdleModeTextPresent(BOOL fIdleModeTextPresent) {

  m_fIdleModeTextPresent = fIdleModeTextPresent;

  (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, REG_LAST_IDLEMODETEXT, (DWORD)fIdleModeTextPresent);
}

//////////////////////////////////////////////////////////////////////////////
//
// Trace the binary data of the SIMToolkit blob passed tt the MMI.
//
// Params:
//  pBlob : Pointer to the blob
//  dwSize: Size of the blob
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
#ifdef SIMTK_ENABLE_BIN_TRACE
void CSimToolkitHandling::TraceBlob(BYTE* pBlob, DWORD dwSize) {
  RILRetailTrace((TEXT("RilDrv: -----------------------------------------------------------------------\r\n")));
  RILRetailTrace((TEXT("RilDrv: SIM-Toolkit blob data (size: %d)\r\n"), dwSize));
  TCHAR   Buf[1024];
  TCHAR*  pBuf = Buf;
  BOOL    tr = FALSE;
  for (DWORD i = 1; i <= dwSize; i++) {
    _stprintf(pBuf, TEXT("0x%02X "), pBlob[i-1]);
    pBuf += 5;
    if (i && !(i % 16)) {
      swprintf(pBuf, TEXT("\r\n"));
      RILRetailTrace((Buf));
      pBuf = Buf;
      tr = TRUE;
    } else {
      tr = FALSE;
    }
  }
  if (!tr) {
    _stprintf(pBuf, TEXT("\r\n"));
    RILRetailTrace((Buf));
  }
  RILRetailTrace((TEXT("RilDrv: -----------------------------------------------------------------------\r\n")));
}
#else
void CSimToolkitHandling::TraceBlob(BYTE* /*pBlob*/, DWORD /*dwSize*/) {
}
#endif // SIMTK_ENABLE_BIN_TRACE

#endif // RIL_ENABLE_SIMTK

