/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/callctrl.cpp $
 * $Revision: /main/ms-d/155 $
 *     $Date: 2014-05-08 10:31:45 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/callctrl.cpp$ 
 * 
 * /main/ms-d/155   2014-05-08 10:31:45   pmackent 
 * Move AT^SAIC to RIL initialization (IPIS100096235)
 *
 * /main/ms-d/154   2014-02-13 11:36:11   pmackent
 * Remove wrong tab characters
 *
 * /main/ms-d/153   2014-02-13 11:26:11   pmackent
 * Fix for problems with CE7 2-way-calls
 *
 * /main/ms-d/152   2013-09-30 15:19:47   pmackent
 * Special handling for AT+CHUP for EHS5
 *
 * /main/ms-d/151   2013-08-21 13:18:03   pmackent
 * Sprint: warning for roaming costs (IPIS100090678)
 *
 * /main/ms-d/150   2013-07-25 16:43:01   pmackent
 * Fix for sporadic fail in Tsp_CallBusy testcase in CE6/7
 *
 * /main/ms-d/149   2013-07-08 17:46:18   pmackent
 * Corrected calls of RIL_DeactivateVoicePath() (IPIS100089519)
 *
 * /main/ms-d/148   2013-07-08 14:54:18   pmackent
 * Workaround for missing quotation mark in AT+CLCC response (IPIS100089511)
 *
 * /main/ms-d/147   2013-06-27 17:50:18   pmackent
 * Workaround for wrong "null" response in EHS5
 *
 * /main/ms-d/146   2013-06-18 08:36:52   pmackent
 * Workaround for wrong status of incoming CDMA voice call
 *
 * /main/ms-d/145   2013-06-17 10:50:48   pmackent
 * Workaround for wrong CDMA data call direction
 *
 * /main/ms-d/144   2013-06-12 14:22:56   pmackent
 * Workaround for wrong "#777" in incoming voice call (IPIS100088854)
 *
 * /main/ms-d/143   2013-05-21 11:44:36   pmackent
 * Don't dial CDMA voice calls, if there is no coverage (IPIS100087446)
 *
 * /main/ms-d/142   2013-05-17 15:59:58   pmackent
 * Removed wrong "!"
 *
 * /main/ms-d/141   2013-05-15 15:25:28   pmackent
 * Temporary workaround for wrong AT+CLCC in EVDO
 *
 * /main/ms-d/140   2013-05-13 14:12:21   pmackent
 * Optional alternative handling of AT+CDV response (IPIS100087446)
 *
 * /main/ms-d/139   2013-05-06 15:16:05   pmackent
 * Prevent-USB-Suspend-Workaround
 *
 * /main/ms-d/138   2013-05-06 15:07:12   pmackent
 * Save old phonebook selection for blacklist deletion (EHS5)
 *
 * /main/ms-d/137   2013-05-03 09:37:17   pmackent
 * Moved end of data mode check
 *
 * /main/ms-d/136   2013-04-30 14:30:43   pmackent
 * New delete blacklist command for EHS5
 *
 * /main/ms-d/135   2013-04-29 16:20:21   pmackent
 * Improved deactivation of data connections
 *
 * /main/ms-d/134   2013-04-24 11:44:33   pmackent
 * New CDMA/CNMI setting and CDMA/CLCC workaround
 *
 * /main/ms-d/133   2013-04-23 11:34:02   pmackent
 * Workarounds for AT^SFLASH / AT^SIND=rssi and AT+CLCC
 *
 * /main/ms-d/132   2013-04-12 09:21:41   pmackent
 * Adaptions for PXS8 2.910
 *
 * /main/ms-d/131   2013-03-26 13:46:09   pmackent
 * Send additional RING notification in CE6
 *
 * /main/ms-d/130   2013-03-21 16:15:30   pmackent
 * Send ATH on data port only (PHS8, PXS8)
 *
 * /main/ms-d/129   2013-03-21 14:31:13   pmackent
 * Handle CDMA datacall + voice call in CE6 (IPIS100085910)
 *
 * /main/ms-d/128   2013-03-20 15:32:13   pmackent
 * Change handling of data calls in CDMA in Windows Mobile (IPIS100085910)
 *
 * /main/ms-d/127   2013-03-04 13:19:26   pmackent
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/126   2013-02-26 15:07:25   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/125   2013-01-14 10:33:37   pmackent
 * Allow update of multiple NV items
 *
 * /main/ms-d/124   2012-12-12 15:23:53   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/123   2012-11-29 15:30:54   pmackent
 * Adaptions call control for EHS5 under CE6
 *
 * /main/ms-d/122   2012-11-27 13:56:25   pmackent
 * Changed timing for voice call status for the EHS5
 *
 * /main/ms-d/121   2012-11-23 09:22:54   pmackent
 * Increase CLCC delay for the EHS5
 *
 * /main/ms-d/120   2012-11-21 13:30:05   pmackent
 * Basic support of EHS5 Rel2
 *
 * /main/ms-d/119   2012-10-30 14:36:10   pmackent
 * Fix call status problems (voice and data) in CDMA on WinCE6/7
 *
 * /main/ms-d/118   2012-10-24 10:55:29   pmackent
 * Allow different audio configuration for CDMA and GSM
 *
 * /main/ms-d/117   2012-09-14 16:25:07   pmackent
 * Fix for missing indication of end of voice call (IPIS100082280)
 *
 * /main/ms-d/116   2012-08-28 09:45:05   pmackent
 * DTMF workaround for PXS8 3.000
 *
 * /main/ms-d/115   2012-08-27 16:17:23   pmackent
 * Change handling of end of data call in CDMA
 *
 * /main/ms-d/114   2012-08-27 08:49:18   pmackent
 * Workaround for wrong AT+VTS error message.
 *
 * /main/ms-d/113   2012-07-13 11:35:20   pmackent
 * Added delay for voice calls
 *
 * /main/ms-d/112   2012-07-06 11:29:44   pmackent
 * Allow PPP traces on CSD/CDMA
 *
 * /main/ms-d/111   2012-07-02 14:57:24   pmackent
 * Filter out "T" modifier in CDMA (IPIS100080194)
 *
 * /main/ms-d/110   2012-06-13 10:43:56   pmackent
 * Added support for CDMA flash
 *
 * /main/ms-d/109   2012-05-16 10:39:05   pmackent
 * Handle negativ CLCC index numbers in CDMA
 *
 * /main/ms-d/108   2012-04-19 08:13:10   ctna0256
 * Don't use AT+CMUT? outside active voice call (IPIS100078740)
 *
 * /main/ms-d/107   2012-04-18 15:55:29   ctna0256
 * Special handling of line status for CDMA
 *
 * /main/ms-d/106   2012-04-18 13:46:43   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/105   2012-03-23 10:49:00   ctna0256
 * Modified EHS5 call state query delay
 *
 * /main/ms-d/104   2012-02-27 10:55:57   ctna0256
 * Handle EHS5 specilities with DTMF, RLP, call control
 *
 * /main/ms-d/103   2012-02-22 13:26:24   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/102   2012-02-17 13:05:18   ctna0256
 * Always use data port for CSD
 *
 * /main/ms-d/101   2012-02-08 15:15:59   ctna0256
 * Add Intel-style SAT
 *
 * /main/ms-d/99   2012-01-31 16:38:14   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/98   2012-01-24 10:26:20   ctna0256
 * Small adaptions for CDMA
 *
 * /main/ms-d/97   2012-01-13 10:09:51   ctna0256
 * Implement audio workaround with SNFO/SNFI set to 0 (IPIS100076299)
 *
 * /main/ms-d/96   2012-01-12 15:22:54   ctna0256
 * Some basic CDMA adaptions
 *
 * /main/ms-d/95   2012-01-06 14:34:10   ctna0256
 * New workaround for SNFO/SNFI settings (IPIS100076299)
 *
 * /main/ms-d/94   2011-12-22 10:45:23   ctna0256
 * Optional separation of audio commands (IPIS100076299)
 *
 * /main/ms-d/93   2011-10-24 11:55:36   ctna0256
 * Workaround for missing AT+CLCC output
 *
 * /main/ms-d/92   2011-09-26 13:34:05   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/91   2011-06-17 10:40:54   ctna0256
 * New API for extended registration status (IPIS100073242)
 *
 * /main/ms-d/90   2011-05-30 10:11:48   ctna0256
 * Avoid SLCC on PH8 Rel1
 *
 * /main/ms-d/89   2011-04-28 13:37:04   ctna0256
 * Use CHUP instead of ATH in case of release all calls.
 *
 * /main/ms-d/88   2011-03-29 08:58:12   ctna0256
 * Cange default emergency call timeout to 0
 *
 * /main/ms-d/87   2011-03-17 10:07:37   ctna0256
 * Abort dial attempt for emergency calls after timeout (IPIS100070746)
 *
 * /main/ms-d/86   2011-03-11 14:49:33   ctna0256
 * Change handling for "busy" test case in CE7
 *
 * /main/ms-d/85   2011-03-10 16:31:04   ctna0256
 * Additional disconnect notification for CE7
 *
 * /main/ms-d/84   2011-03-09 14:24:29   ctna0256
 * Run code analysis on Windows CE7
 *
 * /main/ms-d/83   2011-01-26 16:39:11   ctna0256
 * Changed handling of SAT setup call numbers
 *
 * /main/ms-d/82   2010-12-15 10:43:47   ctna0256
 * Performed lint check
 *
 * /main/ms-d/81   2010-12-07 14:12:15   ctna0256
 * Send reject CSD call on standard instead of data port
 *
 * /main/ms-d/80   2010-12-07 13:35:49   ctna0256
 * Special handling of rejecting CSD calls
 *
 * /main/ms-d/79   2010-11-18 11:11:55   ctna0256
 * New notifications for dial and hangup of emergency calls
 *
 * /main/ms-d/78   2010-11-17 15:57:24   ctna0256
 * Replace ATH with SHUP
 *
 * /main/ms-d/77   2010-10-18 09:05:12   ctna0256
 * No Fax for PH8
 *
 * /main/ms-d/76   2010-09-22 11:38:37   ctna0256
 * Disable SNFO reset on PH8 customer sample per default
 *
 * /main/ms-d/75   2010-09-03 10:54:28   ctna0256
 * Integrate CE7 commands for call management
 *
 * /main/ms-d/74   2010-07-14 10:40:37   ctna0256
 * Preparation: CCUG and ACM may not be supported in PH8
 *
 * /main/ms-d/73   2010-07-02 15:58:33   ctna0256
 * Introduce SAIC-settings and modified SNFO for PH8
 *
 * /main/ms-d/72   2010-05-27 15:19:24   ctna0256
 * Handle case 1st call ID not equal 1, but 2nd call ID is.
 *
 * /main/ms-d/71   2010-05-21 14:59:47   ctna0256
 * Fix for renumbered call IDs in multiparty call under CE6
 *
 * /main/ms-d/70   2010-05-19 13:26:02   ctna0256
 * Fix for missing call progress notification for waiting call.
 * Fix for exception in manage call function.
 *
 * /main/ms-d/69   2010-04-30 15:29:06   ctna0256
 * Automatically switch to 2G for CSD connections (IPIS100064182)
 *
 * /main/ms-d/68   2010-04-23 10:20:41   ctna0256
 * Workaround for increasing call IDs after GPRS
 *
 * /main/ms-d/67   2010-04-16 14:22:17   ctna0256
 * Corrected call progress disconnect notification for CE6
 *
 * /main/ms-d/66   2010-03-10 16:04:38   ctna0256
 * Add missing disconnect message for CE6
 *
 * /main/ms-d/65   2010-02-24 13:25:47   ctna0256
 * Made CE6 driver comply with radiometrics test
 *
 * /main/ms-d/64   2010-02-17 14:27:04   ctna0256
 * GetRegistrySZ() might return TRUE, even on error (IPIS100062294)
 *
 * /main/ms-d/63   2010-02-03 10:50:37   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/62   2010-01-20 11:35:04   ctna0256
 * No SHUP for HC25 Rel1
 *
 * /main/ms-d/61   2009-12-18 10:57:32   ctna0256
 * Translate **002 and **004 code (IPIS100061138)
 *
 * /main/ms-d/60   2009-10-07 09:22:14   ctna0256
 * Introduce no-operation command queue
 * Support 2nd alpha identifier in SAT setup call (IPIS100055375)
 *
 * /main/ms-d/59   2009-07-21 10:19:33   ctna0256
 * Reg-Key to dial certain numbers as 112 (IPIS100056814)
 *
 * /main/ms-d/58   2009-07-13 09:34:44   ctna0256
 * Removed unnecessary extern declaration
 *
 * /main/ms-d/57   2009-07-06 09:56:18   ctna0256
 * Workaround for wrong busy tone (IPIS100056689)
 *
 * /main/ms-d/56   2009-06-18 16:17:21   ctna0256
 * Alllow paket data even while initialization
 *
 * /main/ms-d/55   2009-05-20 09:30:41   ctna0256
 * Removed unused command option
 *
 * /main/ms-d/54   2009-05-05 10:21:47   ctna0256
 * Deactivate AT^SNFO and AT^SNFI with registry key.
 *
 * /main/ms-d/53   2009-02-23 13:15:52   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/52   2009-01-22 10:17:21   ctna0256
 * Optional re-call of RHA_ConfirmAudioMode after RHA_ActivateVoicePath
 *
 * /main/ms-d/51   2009-01-16 14:36:39   ctna0256
 * Dial data option for incoming CSD call
 *
 * /main/ms-d/50   2009-01-16 12:44:19   ctna0450
 * New mechanism to prevent PPP data from slip into the AT response buffer
 *
 * /main/ms-d/49   2008-12-03 10:50:35   ctna0256
 * Configure reject cause with registry
 *
 * /main/ms-d/48   2008-12-02 15:34:09   ctna0256
 * Set call waiting before making CSD
 *
 * /main/ms-d/47   2008-12-02 14:12:01   ctna0256
 * Special reject cause for voice calls
 *
 * /main/ms-d/46   2008-12-01 12:02:17   ctna0256
 * Reject incoming video calls automatically
 *
 * /main/ms-d/45   2008-11-19 10:18:34   ctna0256
 * New registry key for additional internal emargency numbers
 * (this is needed for the Korea version of the HC25)
 *
 * /main/ms-d/44   2008-11-17 13:22:07   ctna0256
 * Use AT^SHUP when call is dialing or alerting
 *
 * /main/ms-d/43   2008-10-21 10:02:58   ctna0256
 * Query status of call meter after unsuccessful dial attempt
 *
 * /main/ms-d/42   2008-10-02 14:17:37   ctna0256
 * Increase priority of call list command to avouid problems with conference call tests
 *
 * /main/ms-d/41   2008-09-23 16:14:56   ctna0256
 * Mute during VTS (optional)
 *
 * /main/ms-d/40   2008-08-22 10:22:41   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/39   2008-08-21 09:33:56   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/38   2008-07-22 10:45:10   mp49210
 * Changed handling of emergency calls for Korea
 *
 * /main/ms-d/37   2008-07-14 13:21:27   mp49210
 * No emergency number translation when PIN is entered
 *
 * /main/ms-d/36   2008-06-20 11:05:36   mp49210
 * Remove unused SIEMENS_MAGNETO define
 *
 * /main/ms-d/35   2008-05-15 15:02:02   mp49210
 * Use ECC configuration from radio
 *
 * /main/ms-d/34   2008-05-13 11:31:01   mp49210
 * Use emergeny call handling for get call list, only if the normal call handling is not possible.
 *
 * /main/ms-d/33   2008-04-16 16:24:02   mp49210
 * Debug zones for RHA traces
 *
 * /main/ms-d/32   2008-04-03 14:41:21   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/31   2008-03-31 13:34:53   mp49210
 * IPIS100017065: delay for automatic SAT responses
 * Support of new SSTR parameters
 * Support of 'no icon' option
 *
 * /main/ms-d/30   2008-03-14 11:39:37   mp49210
 * IPIS100045649: special handling of BUSY for SAT setup call handling
 *
 * /main/ms-d/29   2008-02-29 16:25:41   mp49210
 * Remove potential use of NULL pointer
 *
 * /main/ms-d/28   2008-02-11 09:18:14   mp49210
 * Remove LINT warnings
 *
 * /main/ms-d/27   2008-01-24 15:57:36   kr36712
 * integration GPS for MTLR and emergency call
 *
 * /main/ms-d/26   2007-12-12 16:17:45   mp49210
 * Merge SAT Setup Call handling from Classic RIL
 *
 * /main/ms-d/25   2007-11-27 11:45:48   mp49210
 * Introduction: blacklist GSM 02.07
 *
 * /main/ms-d/24   2007-11-26 10:35:00   mp49210
 * Fix for not detected end of call
 *
 * /main/ms-d/23   2007-10-11 16:23:21   mp49210
 * Introduction of AT^SLCC
 *
 * /main/ms-d/22   2007-10-09 09:59:56   mp49210
 * Simulating missing error at AT+CMUT
 *
 * /main/ms-d/21   2007-09-27 16:17:05   mp49210
 * Misplaced comma in AT+CLCC in HC28
 *
 * /main/ms-d/20   2007-06-28 09:58:44   mp49210
 * Close handle to threads to prevent handle leaks in memory
 *
 * /main/ms-d/19   2007-05-14 13:43:54   mp49210
 * IPIS100036178: map 19+<send> to AT+CHUP
 *
 * /main/ms-d/18   2007-03-28 13:47:55   mp49210
 * Remove 'Classic RIL' specific code and comments
 *
 * /main/ms-d/17   2007-03-28 11:31:38   mp49210
 * Workaround for missing '0' in AT+CLCC
 *
 * /main/ms-d/16   2007-03-26 14:57:09   mp49210
 * Minimum difference between +CLIP and AT+CLCC
 *
 * /main/ms-d/15   2007-03-09 14:40:33   mp49210
 * Fix for fast dial/abort of voice call.
 *
 * /main/ms-d/14   2007-02-16 16:11:18   mp49210
 * New default emergency numbers
 *
 * /main/ms-d/13   2007-02-09 15:15:35   mp49210
 * Lint compatibility
 *
 * /main/ms-d/12   2007-01-11 15:23:28   mp49210
 * Force call state check for AT+CPAS/+CCWA workaround
 *
 * /main/ms-d/11   2007-01-03 17:00:36   mp49210
 * Changes for problems with emergency call without SIM and multiparty calls.
 *
 * /main/ms-d/10   2007-01-02 15:03:29   mp49210
 * Remove some features not supported by Quinn
 *
 * /main/ms-d/9   2006-12-28 15:43:30   mp49210
 * Change ports for ATA
 *
 * /main/ms-d/8   2006-12-27 14:20:14   mp49210
 * Workaround for wrong CME-error at AT+CPBR
 *
 * /main/ms-d/7   2006-12-21 18:26:50   mp49210
 * Changed handling of call states
 *
 * /main/ms-d/6   2006-12-15 17:12:40   mp49210
 * Update calll management capabilities.
 * Prevent 2G/3G switch while active call.
 *
 * /main/ms-d/5   2006-11-30 14:02:32   mp49210
 * Workaround for AT+CPAS bug
 *
 * /main/ms-d/4   2006-11-10 12:00:13   mp49210
 * Set correct parameter type for multiparty
 *
 * /main/ms-d/3   2006-11-10 09:45:40   mp49210
 * Substitute for AT+VTD, new AT+VTS syntax
 *
 * /main/ms-d/2   2006-11-02 09:37:47   mp49210
 * No AT^SLCC in Quinn
 *
 * /main/ms-d/1   2006-10-25 16:52:49   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"
#include <rilbacklight.h>
#include "ccoreutl.h"

#if 0
//
// Line status values
//
static const DWORD g_rgdwLineStats[] =
{
    RIL_LINESTAT_READY,             // 0
    RIL_LINESTAT_UNAVAILABLE,       // 1
    RIL_LINESTAT_UNKNOWN,           // 2
    RIL_LINESTAT_RINGING,           // 3
    RIL_LINESTAT_CALLINPROGRESS,    // 4
    RIL_LINESTAT_ASLEEP,            // 5
};
#define NUM_LINESTATS   (sizeof(g_rgdwLineStats) / sizeof(DWORD))
#endif

// Defines an arbitrary maximum string length for the dial command.
// This maximum is not strictly enforced. It is used only as an aid
// for reporting a more specific error (RIL_E_DIALSTRINGTOOLONG) if
// the radio returns an error that is too generic.
#define MAX_DIAL_STRING_LENGTH      43

// Limits the maximum amount of DTMF tones which can be played with one AT command.
#define MAX_DTMF_TONES_PER_LINE     54

// Limits the maximum number of DTMF tones in a AT+VTS-string.
#define MAX_DTMF_TONES_PER_CMD      1

// Buffer size to store array with emergency numbers.
#define EMNO_BUFFER                256

// Number used for emergency calls, if correct number is not available.
#define DEFAULT_EMERGENCY_NUMBER  "112"

// This timeout (in ms) is the default timeout for an emergency call
// without SIM to succeed. "0" means: timeout not in use.
#define DEFAULT_EMERGENCY_DIAL_TIMEOUT  0

// Relevant for SHOULD_WAIT_AFTER_LAST_VOICECMD workaround: default
// waiting period in ms.
#define DEFAULT_WAITAFTERLASTVOICECMD_PERIOD 650

// On some occasions it is necessary for the RIL to poll for information.
// This time (in ms) is the polling interval.
#define CALLCTRL_DEFAULT_POLLING_INTERVAL   100
#define CALLCTRL_FAST_POLLING_INTERVAL      50

#define CALLSTATS_CHK_NOCHECK     0
#define CALLSTATS_CHK_CHECK       1
#define CALLSTATS_CHK_ALPHACHECK  2
#define CALLSTATS_CHK_FORCE_CHECK 3

#define WAIT_FOR_SLCC_AFTER_OK   250

BOOL g_fCallWaiting = FALSE;
BOOL g_fEmergencyCall = FALSE;
static char g_szEmergencyNo[MAX_EMERGENCYNO];
UINT g_uiECCCacheSize = 0;
char g_szECCCache[ECC_CACHE_SIZE][MAX_EMERGENCYNO];
BOOL g_fECCFieldAvailable = FALSE;
static BOOL g_fSATCallActive = FALSE;
DWORD g_dwLastVoiceCmd = 0;
static DWORD s_dwDuration = 300;

static HRESULT ParseGetCallList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam);
static HRESULT ParseATHError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/,LPVOID pParam, DWORD& /*dwCode*/);
static HRESULT ParseATHOK(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam);
static BOOL g_fAudioPathActivated = FALSE;
static DWORD g_dwAudioSelection = 0;
static BOOL g_bForceSetting = FALSE;
BOOL g_fReleaseHeldCall = FALSE;
extern BOOL g_fNoMuteCmdSoFar;
extern UINT g_uiECCModule;
extern RHA_FUNCTION_LIST RHAFunctionList;

// Memory block to hold the current list of active calls. The RIL could query this
// list any time with the AT+CLCC command, but in order to avoid sending unnecessry
// commands, it holds the current list in memory. This list is filled with the content
// of the latest ^SLCC: URC or AT+CLCC command sent.
RILCALLINFO g_CallInfo[MAX_NUMBERCALLS];
// Number of current calls (-1: current call status is unknown)
INT g_iNrCallInfo = -1;
// Used for workaround for the incorrect numbered voice calls. If this bug is
// presend the radio would not give the first call the number 1. This variable
// hold the index the module assigned to the first call. -1 means, this workaround
// is not used.
INT g_iIdxForFirstCall = -1;
// Is set to TRUE, if the current list of calls may be wrong. Wet in various
// situations where the module is know to send ^SLCC URCs with wrong content.
BOOL g_bNeedCallInfoRecheck = FALSE;
// Critical section used to avoid that two task simultaniously working on the
// variables above.
CRITICAL_SECTION g_CallInfoSection;

// Time (ms) of the last ATD command used.
DWORD g_LastATD = 0;
// Time (ms) of the last +CLIP URC received.
DWORD g_dwLastCLIPURC = 0;
// Set to TRUE, if there is an emergency call currently ongoing.
static BOOL g_fEmergencyCallOngoing = FALSE;

#ifdef RILDRV_CE6
// Store the index of the last call to be disconnected
INT g_iLastIdxDisconnected = 0;
RILCALLINFO g_oLastCIDisconnected;
// Store last knwon disconnect cause
DWORD g_dwLastDisconnectCause = 0;
// CDMA: is set to >0, if a CDMA data connection is about to be ended locally.
static INT s_nCDMAHangupIdx = 0;
// As above, but for answering incoming calls.
static INT s_nCDMAAnswerIdx = 0;
#endif

// Possible additional ECC numbers to be configured with AT^SCFG=Call/ECC
static const LPCSTR g_rgszAdditionECCNumbers[] =
{
    "000",
    "08",
    "110",
    "118",
    "119",
    "999",
};
#define NUM_ADDITIONECCNUMBERS    (sizeof(g_rgszAdditionECCNumbers) / sizeof(LPCSTR))

// The USB-Suspend workaround: AT timeout and stop event.
// See functionStartNoUsbSuspendThread()
static DWORD s_dwNoUsbSuspendTimeout = 0;
static HANDLE s_hStopNoUsbSuspendThread = INVALID_HANDLE_VALUE;

//
// In order to prevent the USB to go into suspend during voice calls, the RIL
// sends a simple "AT" periodically. See "NoUsbSuspendInterval" key.
//
static DWORD WINAPI NoUsbSuspendThreadProc(LPVOID pVoid)
{
    CRilHandle *pHandle = (CRilHandle *)pVoid;

    // Until the stop event has been set (voice call ended) or the event has been
    // canceld (RIL shutdown) send periodically an "AT".
    while (WaitForSingleObject(s_hStopNoUsbSuspendThread, s_dwNoUsbSuspendTimeout) == WAIT_TIMEOUT) {
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT\r", CMDOPT_NONE, APIID_NONE, s_dwNoUsbSuspendTimeout, 0, 0))
            break;
    }

    // Thread ended, the stop event is no longer needed.
    CloseHandle(s_hStopNoUsbSuspendThread);
    s_hStopNoUsbSuspendThread = INVALID_HANDLE_VALUE;
    return 0;
}

//
// Option: in order to prevent the USB to go into suspend during voice calls,
// the RIL will send periodically an "AT" to the module (if configured with
// registry key "NoUsbSuspendInterval").
//
static void StartNoUsbSuspendThread(CRilHandle *pHandle)
{
    GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoUsbSuspendInterval"), &s_dwNoUsbSuspendTimeout);

    if (s_dwNoUsbSuspendTimeout > 0) {
        if (s_hStopNoUsbSuspendThread == INVALID_HANDLE_VALUE)
            s_hStopNoUsbSuspendThread = CreateEvent(NULL, TRUE, FALSE, NULL);

        CloseHandle(CreateThread(NULL, 0, NoUsbSuspendThreadProc, (LPVOID)pHandle, 0, 0));
    }
}

//
// Stop USB suspend thread, see functionStartNoUsbSuspendThread().
//
static void StopNoUsbSuspendThread(void)
{
    // Set stop event. The thread will close the event if it is no longer
    // needed.
    if (s_hStopNoUsbSuspendThread != INVALID_HANDLE_VALUE)
        SetEvent(s_hStopNoUsbSuspendThread);
}

//
// EndCSDDataMode()
// Needed for CDMA: forces the end of an active data mode in order to send
// ATH or ATD#777 command.
//
static void EndCSDDataMode(CRilHandle *pHandle)
{
    CComHandle *pDataPort = NULL;
    DWORD dwStartTime = GetTickCount();
    DWORD dwTimeout = 5000;

    // Get handle to data port.
    pDataPort = pHandle->GetComDevice(DATA_PORT);

    // The Windows PPP stack has already sent a LCP terminate request,
    // so the data mode will probably end soon on its own.
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("TimeoutWaitingForATHCmdMode"), &dwTimeout);
    while (GetTickCount() - dwStartTime < dwTimeout) {
        // Check for data/cmd mode on data port.
        if (!pDataPort->FDataMode()) break;
        Sleep(100);
    }

    // If the data mode has not been ended on its own, try to switch
    // the port back to command mode.
    if (pDataPort->FDataMode())
        (void)pDataPort->SwitchBackToCommandMode();
}

//
// Feature: if configured with registry, the RIL forces the radio to 2G, when a
// CSD connection is established.
//
static void EnsureRightCSDBandConfiguration(CRilHandle *pDevice)
{
    // Perform band configuration to 2G only if RIL is not set to UMTS-only mode.
    if (pDevice->Force2GforCSD() && g_n2G3GSelection != SELECTION2G3G_UMTS_ONLY) {
        DWORD dwACT = NO_INIT_COPS_SETTING;

        // Wuery the current selection, radio might already be registered to 2G.
        (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pDevice, "AT+COPS?\r", CMDOPT_NONE, APIID_GETCURRENTOPERATOR, 0, 0, 0, ParseGetCurrentOperator, NULL, (LPVOID)&dwACT);
        if (dwACT == 2) {   // Currently registered to 3G?
            DWORD dwTimeout = 15000;
            DWORD dwStartTime;
            DWORD dwRegStatus = g_dwRegStatus;

            GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("Force2GforCSDTimeout"), &dwTimeout);

            // The RIL must temporarily block the registration notifications. If the connection manager
            // would get such a notification it would immediately terminate the dial command.
            pDevice->BlockRegistrationNotification(TRUE);
            // Configure radio to 2G radio bands only.
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pDevice, "AT^SCFG=Radio/Band,\"15\",\"1\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
            // Unfortunately, there is no event mechanism, which informs about
            // registration state change, so the RIL must use polling here to wait
            // wait for the 2G registration.
            dwStartTime = GetTickCount();
            // First wait for deregistration.
            while (GetTickCount() - dwStartTime < dwTimeout) {
                if (g_dwRegStatus != RIL_REGSTAT_HOME &&  g_dwRegStatus != RIL_REGSTAT_ROAMING)
                    break;
                Sleep(CALLCTRL_DEFAULT_POLLING_INTERVAL);
            }
            // Then wait for registration.
            while (GetTickCount() - dwStartTime < dwTimeout) {
                if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
                    break;
                Sleep(CALLCTRL_DEFAULT_POLLING_INTERVAL);
            }
            pDevice->BlockRegistrationNotification(FALSE);

            if (dwRegStatus != g_dwRegStatus) {
                // Just in case the regitration status after the operation is different to the original one,
                // send the new one.
                pDevice->GenerateRegStatusNotification(g_dwRegStatus);
            }
        }
    }
    return;
}

BOOL IsACallDialing(CRilHandle *pDevice)
{
    BOOL bCallActive = FALSE;

    if (!SUPPORTS_SLCC || g_iNrCallInfo < 0 || AVOID_SLCC)
        (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pDevice, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListForceCheck, NULL, (LPVOID)pDevice);

    if (g_iNrCallInfo > 0) {
        EnterCriticalSection(&g_CallInfoSection);
        if (g_iNrCallInfo > 0) {
            INT i;

            for (i=0; i<g_iNrCallInfo; i++) {
                if (g_CallInfo[i].dwStatus==RIL_CALLSTAT_DIALING) {
                    bCallActive = TRUE;
                    break;
                }
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);
    }

    return bCallActive;
}

void RHAActivateVoicePath(CRilHandle * pHandle)
{
    if (!g_fAudioPathActivated)
    {
        g_fAudioPathActivated = TRUE;
        (void)RHA_ActivateVoicePath(pHandle->GetAudioTxDevice(), pHandle->GetAudioRxDevice());

        StartNoUsbSuspendThread(pHandle);

        // Some customer implementations of the RHA may not handle the case
        // when the call of RHA_ConfirmAudioMode() comes before the call of
        // RHA_ActivateVoicePath(). If the RIL detects this case, if repeats the
        // RHA_ConfirmAudioMode() function call afterwards.
        if (g_dwAudioSelection > 0 && !g_fNoAudioSettings)
        {
            if (RHAFunctionList.pfnRHA_ConfirmAudioMode != NULL)
            {
                DWORD dwExtMicAudioCodecVol, dwExtAudioCodecVol;
                DWORD dwTxDevice, dwRxDevice;
                DWORD dwDisableSetSNFS = 0;
                DWORD dwResetAudio = 0;
                BOOL bRHAMode;

                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Repeat call of RHA_ConfirmAudioMode()\r\n")));
                dwResetAudio = (AVOID_RESET_OF_SNFO_FOR_MODE_CHANGE ? 0 : 1);
                (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableSetSNFS"), &dwDisableSetSNFS);
                if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ResetAudioBeforeRHAConfirm"), &dwResetAudio))
                    dwResetAudio = ((g_dwAlwaysZeroAudioParameters == 1) ? 0 : 1);

                if (dwDisableSetSNFS != 1 && !g_fNoAudioSettings && dwResetAudio == 1) {
                    INT iDevice = GetAudioTableIndex(RIL_AUDIO_HANDSET);
                    char szResetCmd[MAX_ATCMD_LEN];
                    char* szWork;
                    AUDIO_CONFIG *pAudio = GetAudioTableEntry(iDevice, pHandle);

                    if (g_dwAlwaysZeroAudioParameters == 1)
                        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, GetResetAudioParams(TRUE), CMDOPT_NONE, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);

                    (void)_snprintfz(szResetCmd, MAX_ATCMD_LEN, "AT^SNFS=%d\r", pAudio->dwAudioMode ? pAudio->dwAudioMode : 1);
                    szWork = strchr(szResetCmd, '\r');

                    if (g_dwSeparateAudioATCmds != 1) {
                        if (SUPPORTS_SAIC_AUDIO_CONFIG && !NO_SAIC_IN_CALL && pAudio->dwSAICinterface > 0 && pAudio->dwAudioMode >= 2) {
                            (void)_snprintfz(szWork, MAX_ATCMD_LEN - (szWork - szResetCmd), ";^SAIC=%u,%u,%u,%u,%u,%u,%u\r", pAudio->dwSAICinterface, pAudio->dwSAICmicrophone, pAudio->dwSAICearpiece, pAudio->dwSAICclock, pAudio->dwSAICmsMode, pAudio->dwSAICframeMode, pAudio->dwSAICextClkMode);
                            szWork = strchr(szResetCmd, '\r');
                        }

                        if (g_dwUseSNFOSNFI == 1 && pAudio->dwAudioMode >= 2) {
                            (void)_snprintfz(szWork, MAX_ATCMD_LEN - (szWork - szResetCmd), ";%s\r", GetResetAudioParams(FALSE));
                            szWork = strchr(szResetCmd, '\r');
                        }
                    }
                    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szResetCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
                    if (g_dwSeparateAudioATCmds == 1) {
                        (void)_snprintfz(szResetCmd, MAX_ATCMD_LEN, "AT%s\r", GetResetAudioParams(FALSE));
                        szWork = strchr(szResetCmd, '\r');
                        if (SUPPORTS_SAIC_AUDIO_CONFIG && !NO_SAIC_IN_CALL && pAudio->dwSAICinterface > 0 && g_dwUseSNFOSNFI == 1) {
                            (void)_snprintfz(szWork, MAX_ATCMD_LEN - (szWork - szResetCmd), ";^SAIC=%u,%u,%u,%u,%u,%u,%u\r", pAudio->dwSAICinterface, pAudio->dwSAICmicrophone, pAudio->dwSAICearpiece, pAudio->dwSAICclock, pAudio->dwSAICmsMode, pAudio->dwSAICframeMode, pAudio->dwSAICextClkMode);
                            szWork = strchr(szResetCmd, '\r');
                        }
                        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szResetCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, g_TimeoutCmdInit, 0, 0);
                    }
                }

                dwTxDevice = g_dwAudioSelection;
                dwRxDevice = g_dwAudioSelection;
                bRHAMode = RHA_ConfirmAudioMode(&dwTxDevice, &dwRxDevice);
                if (!bRHAMode || g_bForceSetting) {
                    // The RHA changed the current audio settings, so the RIL has to perform
                    DWORD dwMute = MAXDWORD;
                    DWORD dwDevice;
                    char szCmd[MAX_ATCMD_LEN];
                    char szCmd2[MAX_ATCMD_LEN];
                    AUDIO_CONFIG *pAudio;

                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Audio settings overwritten by RHA from %d/%d to %d/%d\r\n"), g_dwAudioSelection, g_dwAudioSelection, dwTxDevice, dwRxDevice));
                    dwDevice = dwTxDevice;
                    pAudio = GetAudioTableEntry(GetAudioTableIndex(dwDevice), pHandle);
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: SetAudioDevices() - tx device = %d\r\n"), dwDevice));
                    GenerateAudioATCmd(szCmd, szCmd2, dwDevice, pHandle);

                    pHandle->SetAudioTxDevice(dwDevice);

                    dwExtAudioCodecVol = pAudio->dwExtAudioCodecVolSteps[(g_dwCacheDownlinkVolume >> 24) / 51];
                    dwExtMicAudioCodecVol = pAudio->dwExtMicAudioCodecVolSteps[(g_dwCacheUplinkVolume >> 24) / 51];
                    (void)RHA_AdjustRxVolume(dwDevice, dwExtAudioCodecVol);
                    (void)RHA_AdjustTxVolume(dwDevice, dwExtMicAudioCodecVol);

                    if (dwDisableSetSNFS != 1) {
                        if (SNFS_DISABLES_CMUT && IsVoiceCallActive(pHandle)) {
                            // AT^SNFS will reset the status of AT+CMUT, so the RIL has to save the old value and restore it
                            // after applying the new audio mode. This command will fail, when used while no call is active,
                            // the RIL doesn't need to check the commands success.
                            (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle, "AT+CMUT?\r", CMDOPT_NONE, APIID_GETAUDIOMUTING, 0, 0, 0, ParseGetAudioMuting, NULL, (LPVOID)&dwMute);
                        }
                        if (g_dwAlwaysZeroAudioParameters == 1)
                            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, GetResetAudioParams(TRUE), CMDOPT_NONE, APIID_SETAUDIODEVICES, 0, 1, 1000);
                        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIODEVICES, 0, 1, 1000);
                        if (strlen(szCmd2) > 0)    // If configured, send 2nd part of the command
                            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd2, CMDOPT_NONE, APIID_SETAUDIODEVICES, 0, 1, 1000);
                        if (SNFS_DISABLES_CMUT && dwMute == 1) {
                            // If necessary, the AT+CMUT value must be set now. This may fail, if there is no call active.
                            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CMUT=%d\r", dwMute);
                            (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, pHandle, szCmd, CMDOPT_NONE, APIID_SETAUDIOMUTING, 0, 0, 0);
                        }
                    }
                }
            }
            g_dwAudioSelection = 0;
        }
    }
}

//
// Store audio settings, if they have been made while audio is off.
//
BOOL StoreAudioSetting(DWORD dwDevice, BOOL bForceSetting)
{
    BOOL bStore = FALSE;

    // In case the audio is not already activated, store the selected device.
    if (!g_fAudioPathActivated)
    {
        DWORD dwRepeatAudio = 0;

        // This mechanism is only needed for RHA versions, which are not able
        // to handle RHA_ConfirmAudioMode() before RHA_ActivateVoicePath().
        // For this reason this option is configured via registry.
        if(!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RepeatConfirmAudioAfterActivation"), &dwRepeatAudio))
            dwRepeatAudio =  0;
        if (dwRepeatAudio == 1 || bForceSetting)
        {
            g_dwAudioSelection = dwDevice;
            g_bForceSetting = bForceSetting;
            bStore = TRUE;
        }
    }
    return bStore;
}

// Emergency number intern: emergency number which can be handled by the module. These
// number are: 000, 110, 112, 911, 999 and 08.  If no SIM is inserted the HC25 module can
// handle additional numbers ( 118, 119).
static BOOL IsEmergencyNumberIntern(LPCSTR szAddress)
{
    BOOL bEmergencyIntern = FALSE;
    WCHAR wszECC[MAX_ATCMD_LEN];

    if (g_dwSIMLockedState == RIL_LOCKEDSTATE_UNKNOWN)      // The additional emergency numbers only apply if there is no SIM inserted or the SIM is not accessible
        bEmergencyIntern = (!strcmp(szAddress, "911") || !strcmp(szAddress, "112") || !strcmp(szAddress, "08") || !strcmp(szAddress, "110") || !strcmp(szAddress, "000") || !strcmp(szAddress, "118") || !strcmp(szAddress, "119") || !strcmp(szAddress, "999"));
    else
        bEmergencyIntern = (!strcmp(szAddress, "911") || !strcmp(szAddress, "112"));

    memset(wszECC, 0, sizeof(wszECC));

    if (!bEmergencyIntern && GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("InternEmergency"), wszECC, MAX_ATCMD_LEN))
    {
        bEmergencyIntern = IsInList(wszECC, szAddress, L",");
    }
    return bEmergencyIntern;
}

// ECC emergency numbers: The EFECC file of a SIM may contain additional emergency numbers.
// Since the RIL has no chance to read this SIM file, this additional emergency numbers must be
// placed in a registry key.
static BOOL IsEmergencyNumberECC(LPCSTR szAddress)
{
    BOOL bIsEmergencyNumber = FALSE;

    // ECC applies not to ECalls without SIM, so we don't need to look into the registry in this case.
    if (g_dwSIMLockedState != RIL_LOCKEDSTATE_UNKNOWN)
    {
        if (g_uiECCCacheSize == 0)
        {
            // No ECC Numbers on SIM, check radio's default ECC numbers.
            UINT i;

            if (!g_fECCFieldAvailable || ECC_CONFIG_ON_EMPTY_ECC_FIELD) {
                // The AT^SCFG settings only apply, when there is no ECC fielad available.
                for (i = 0; i < NUM_ADDITIONECCNUMBERS; i++) {
                    if ((g_uiECCModule&(1<<i))!=0 && strcmp(szAddress, g_rgszAdditionECCNumbers[i])==0) {
                        bIsEmergencyNumber = TRUE;
                        break;
                    }
                }
            }
        }
        else
        {
            // ECC Numbers on, SIM cached ECC numbers.
            UINT iECCNr = 0;

            while (iECCNr < g_uiECCCacheSize)
            {
                if (strcmp(szAddress, g_szECCCache[iECCNr]) == 0)
                {
                    bIsEmergencyNumber = TRUE;
                    break;
                }
                iECCNr ++;
            }
        }
        if (bIsEmergencyNumber == FALSE)
        {
            WCHAR wszECC[MAX_ATCMD_LEN];

            memset(wszECC, 0, sizeof(wszECC));
            if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ECCEmergency"), wszECC, MAX_ATCMD_LEN))
            {
                bIsEmergencyNumber = IsInList(wszECC, szAddress, L",");
            }
        }
    }
    return bIsEmergencyNumber;
}

// Emergency number: emergency numbers are number know to the module, the ECC emergency numbers
// and the numbers stored in the Microsoft MMI.
static BOOL IsEmergencyNumber(LPCSTR szAddress)
{
    BOOL bIsEmergencyNumber;
#ifndef RILDRV_CE6
    WCHAR wszBuffer[EMNO_BUFFER];
#endif

    // First we test the emergency numbers built in into the modules, since
    // they always available.
    bIsEmergencyNumber = (IsEmergencyNumberIntern(szAddress) || IsEmergencyNumberECC(szAddress));

#ifndef RILDRV_CE6
    // There may be other emergency numbers known to the Microsoft MMI.
    // This doesn't apply to Windows CE6
    memset(wszBuffer, 0, EMNO_BUFFER*sizeof(WCHAR));
    if (!bIsEmergencyNumber && strlen(szAddress)<=MAX_EMERGENCYNO && SUCCEEDED(GetEmergencyCallList(wszBuffer, EMNO_BUFFER)))
    {
        WCHAR wszAddress[MAX_EMERGENCYNO+1];
        INT iPos = 0;

        (void)_snwprintfz(wszAddress, MAX_EMERGENCYNO+1, L"%S", szAddress);
        while (wszBuffer[iPos] != 0)
        {
            if (wcscmp(&wszBuffer[iPos], wszAddress) == 0)
            {
                bIsEmergencyNumber = TRUE;
                break;
            }
            iPos += (INT)wcslen(&wszBuffer[iPos])+1;
        }
    }
#endif
    if (!bIsEmergencyNumber)
    {
        WCHAR wszAddEC[MAX_ATCMD_LEN];

        memset(wszAddEC, 0, sizeof(wszAddEC));
        if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AdditionalEmergencyNo"), wszAddEC, MAX_ATCMD_LEN))
        {
            bIsEmergencyNumber = IsInList(wszAddEC, szAddress, L",");
        }
    }
    if (bIsEmergencyNumber)
    {
        WCHAR wszNoEC[MAX_ATCMD_LEN];

        memset(wszNoEC, 0, sizeof(wszNoEC));
        if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisabledEmergencyNo"), wszNoEC, MAX_ATCMD_LEN))
        {
            bIsEmergencyNumber = IsInList(wszNoEC, szAddress, L",");
        }
    }
    return bIsEmergencyNumber;
}

#ifndef RILDRV_CE6
static BOOL IsDialCommandInQueue(void* pItem, DWORD dwData)
{
    CCommand* pCmd = (CCommand*)pItem;
    BOOL* pFound = (BOOL *)dwData;

    if (pCmd->FDial())
        *pFound = TRUE;

    return FALSE;
}
#endif

// Some numbers configured in registry have to be dialed as the default emergency
// number "112", even when the PIN is already entered.
static BOOL DialAsDefaultEN(LPCSTR szAddress)
{
    BOOL bDialAs112 = FALSE;
    WCHAR wszDialDEN[MAX_ATCMD_LEN];

    memset(wszDialDEN, 0, sizeof(wszDialDEN));
    if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DialAsDefaultEN"), wszDialDEN, MAX_ATCMD_LEN))
    {
        bDialAs112 = IsInList(wszDialDEN, szAddress, L",");
    }
    return bDialAs112;
}

// Set audio shortly before dialing
static HRESULT PreInitDialing(CRilHandle * pHandle, COM_PORT_TYPE /*iPort*/)
{
    CCommand *pCmd;

    // Do not set the audio setting the same time the initial AT+CLCC is running on the URC port.
    while (!g_PowerManager.IsRadioOff()) {
        pCmd = pHandle->GetCurrentCommand();
        if (pCmd != NULL) {
            char *szCmd;

            szCmd = pCmd->GetCmd();
            if (szCmd != NULL) {
                if (strcmp(szCmd, "AT+CLCC\r") != 0)
                    break;
            }
            else
                break;
        }
        else
            break;
        Sleep(25);
    }

    g_LastATD = GetTickCount();
    pHandle->SetSLCCaudioActive(TRUE);
    RHAActivateVoicePath(pHandle);

    return S_OK;
}

// Set audio shortly before answering
static HRESULT PreInitAnswer(CRilHandle * pHandle, COM_PORT_TYPE /*iPort*/)
{
    pHandle->SetSLCCaudioActive(TRUE);
    RHAActivateVoicePath(pHandle);
    g_fCallWaiting = FALSE;

    return S_OK;
}

// We have to save the dialed emergency number (112, 911 or 08) to be able
// do generate the correct faked AT+CLCC response.
static void SetEmergencyNumber(LPCSTR szAddress)
{
    if (szAddress == NULL || strlen(szAddress) >= MAX_EMERGENCYNO)
        (void)strncpyz(g_szEmergencyNo, DEFAULT_EMERGENCY_NUMBER, MAX_EMERGENCYNO);
    else
        (void)strncpyz(g_szEmergencyNo, szAddress, MAX_EMERGENCYNO);
    g_fEmergencyCall = TRUE;
    (void)g_GpsHandling.SwitchGpsStatePassive(TRUE);
}

//
//
//
static HRESULT ParseATDError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam, DWORD& /*dwCode*/)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;

    // Unfortunately the HC25 doesn't tell, if the reason of the failure of the
    // ATD command had been, that the call meter maximum has been reached.
    // To check, if this had been the case, this thread will check the current
    // CEER status.
    if (pHandle != NULL && SUPPORTS_ADVICE_OF_CHARGE)
        CloseHandle(CreateThread(NULL, 0, ACMMaxThreadProc, (LPVOID)pHandle, 0, 0));
    return S_OK;
}

//
//
//
static HRESULT ParseATDCSDError(LPCSTR szRsp, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam, DWORD& /*dwCode*/)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;


    if (szRsp != NULL) {
        // If there is anything elso than a connect message, the dial attempt went wrong.
        if (strstr(szRsp, "CONNECT") == NULL) {
            // If the Force2GforCSD feature is used, the radio band might be set to the wrong settings
            // and must changed back.
            if (pHandle->Force2GforCSD())
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT^SCFG=Radio/Band,\"127\",\"1\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
        }
    }

    return S_OK;
}

#ifdef RILDRV_CE6
INT FindIDinCallInfo(const RILCALLINFO *pCallInfo, INT iNrCallInfo, DWORD dwID)
{
    INT iFound = -1;
    INT iIdx;

    for (iIdx = 0; iIdx < iNrCallInfo; iIdx++) {
        if (pCallInfo[iIdx].dwID == dwID) {
            iFound = iIdx;
            break;
        }
    }

    return iFound;
}

void SendDisconnectNotification(CRilHandle *pRilHandle, DWORD dwCode)
{
    RILCALLINFO oCallInfo[MAX_NUMBERCALLS];
    INT i, iIdx, iID, iCount = 0;

    EnterCriticalSection(&g_CallInfoSection);
    for (iID = 1; iID <= MAX_NUMBERCALLS; iID++) {
        iIdx = FindIDinCallInfo(g_CallInfo, g_iNrCallInfo, iID);
        if (iIdx >= 0) {
            memcpy(&oCallInfo[iCount], &g_CallInfo[iIdx], sizeof(RILCALLINFO));
            iCount++;
        }
    }
    iIdx = FindIDinCallInfo(oCallInfo, iCount, g_iLastIdxDisconnected);
    if (iIdx < 0) {
        memcpy(&oCallInfo[0], &g_oLastCIDisconnected, sizeof(RILCALLINFO));
        iCount = 1;
        iIdx = 0;
    }
    oCallInfo[iIdx].dwParams &= ~RIL_PARAM_CI_STATUS;
    oCallInfo[iIdx].dwParams |= RIL_PARAM_CI_CPISTATUS;
    oCallInfo[iIdx].dwStatus = RIL_CPISTAT_DISCONNECTED;
    oCallInfo[iIdx].dwDisconnectCode = dwCode;
    oCallInfo[iIdx].dwParams |= RIL_PARAM_CI_DISCONNECTCODE;
    g_iLastIdxDisconnected = 0;
    LeaveCriticalSection(&g_CallInfoSection);

    for (i = 0; i < iCount; i++) {
        (void)pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_CALLPROGRESSINFO, (LPVOID)&oCallInfo[i], sizeof(RILCALLINFO));
    }
    return;
}

static void ConvertCallStat2CPIStat(RILCALLINFO *pCallInfo)
{
    pCallInfo->dwParams &= ~RIL_PARAM_CI_STATUS;
    pCallInfo->dwParams |= RIL_PARAM_CI_CPISTATUS;
    switch (pCallInfo->dwStatus)
    {
        case RIL_CALLSTAT_ALERTING:
        case RIL_CALLSTAT_DIALING:
            pCallInfo->dwStatus = RIL_CPISTAT_NEW_OUTGOING;
            break;

        case RIL_CALLSTAT_INCOMING:
        case RIL_CALLSTAT_WAITING:
            pCallInfo->dwStatus = RIL_CPISTAT_NEW_INCOMING;
            break;

        case RIL_CALLSTAT_ACTIVE:
            pCallInfo->dwStatus = RIL_CPISTAT_CONNECTED;
            break;

        case RIL_CALLSTAT_ONHOLD:
            pCallInfo->dwStatus = RIL_CPISTAT_ONHOLD;
            break;

        default:
            pCallInfo->dwStatus = RIL_CPISTAT_UNKNOWN;
            pCallInfo->dwParams &= ~RIL_PARAM_CI_CPISTATUS;
            break;
    }
}

//
// This functions converts the call state info in WM6 style into the
// call progress info in CE6 style and sends it to the OS.
//
// pRilHandle [in]: pointer to current CRILHandle
// pCallInfo [in]: pointer to old call state
// iNrCallInfo [in]: number of elements in pCallInfo
//
void SendCE6CallProgressInfo(CRilHandle *pRilHandle, const RILCALLINFO *pCallInfo, INT iNrCallInfo)
{
    RILCALLINFO oCallInfo[MAX_NUMBERCALLS];
    INT iCount = 0;
    INT i, iIdx, iIdx2, iID;

    memset(oCallInfo, 0, sizeof(oCallInfo));

    EnterCriticalSection(&g_CallInfoSection);
    // Walk through all valid call IDs,
    for (iID = 1; iID <= MAX_NUMBERCALLS; iID++) {
        // Find ID in the old call list
        iIdx = FindIDinCallInfo(pCallInfo, iNrCallInfo, iID);
        // Is ID in the old call list?
        if (iIdx >= 0) {
            // Find same ID in the new call list
            iIdx2 = FindIDinCallInfo(g_CallInfo, g_iNrCallInfo, iID);
            // Is ID also found in the new call list?
            if (iIdx2 >= 0) {
                // Check if this call state has already been sent. The Radiometrics
                // test gets confused, if the same progress notifications shows up
                // twice.
                if (memcmp((LPVOID)(&g_CallInfo[iIdx2]), (LPVOID)(&pCallInfo[iIdx]), sizeof(RILCALLINFO)) != 0) {
                    // Generate a new CE6 style call list entry by copying the current
                    // entry and modify the status value from the CI to the CPI type.
                    memcpy(&oCallInfo[iCount], &g_CallInfo[iIdx2], sizeof(RILCALLINFO));
                    ConvertCallStat2CPIStat(&oCallInfo[iCount]);
                    iCount ++;
                }
            }
            else {
                // Call doesn't exist any more. Save ID, so it can be used, when the
                // disconnect notification is generated.
                g_iLastIdxDisconnected = iID;
                memcpy(&g_oLastCIDisconnected, &pCallInfo[iIdx], sizeof(RILCALLINFO));

                // Special case: incomming call was aborted by caller.
                // In this case there won't be any NO CARRIER indications, which
                // trigger the disconnect notification, so it must be done here.
                if (pCallInfo[iIdx].dwStatus == RIL_CALLSTAT_INCOMING || pCallInfo[iIdx].dwStatus == RIL_CALLSTAT_WAITING)
                    SendDisconnectNotification(pRilHandle, RIL_RESULT_CALLABORTED);
                else if (g_dwLastDisconnectCause != 0)
                    SendDisconnectNotification(pRilHandle, g_dwLastDisconnectCause);
            }
        }
        else {
            // Find same ID in the new call list
            iIdx2 = FindIDinCallInfo(g_CallInfo, g_iNrCallInfo, iID);
            if (iIdx2 >= 0) {
                // Generate a new CE6 style call list entry by copying the current
                // entry and modify the status value from the CI to the CPI type.
                memcpy(&oCallInfo[iCount], &g_CallInfo[iIdx2], sizeof(RILCALLINFO));
                ConvertCallStat2CPIStat(&oCallInfo[iCount]);
                iCount ++;
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    // If nothing CPI relevant is changed, the notification shouldn't be
    // sent, otherwise the radiometrics test is confused.
    for (i = 0; i < iCount; i++) {
        // The TAPI requires the RING notification to be sent at least once
        // before the CALLPROGRESS notification. The notification may already
        // been sent by the RIL, but the TAPI doesn't mind if it appears more
        // than once.
        if (oCallInfo[i].dwStatus == RIL_CPISTAT_NEW_INCOMING) {
            RILRINGINFO rri; memset(&rri,0,sizeof(rri)); // zero struct

            rri.cbSize = sizeof(RILRINGINFO);
            rri.dwParams = RIL_PARAM_RI_ADDRESSID|RIL_PARAM_RI_CALLTYPE;
            rri.dwAddressId = 1;
            rri.dwCallType = RIL_CALLTYPE_UNKNOWN;
            (void)pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_RING, (LPVOID)&rri, sizeof(RILRINGINFO));
        }
        (void)pRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_CALLPROGRESSINFO, (LPVOID)&oCallInfo[i], sizeof(RILCALLINFO));
    }
    if (g_dwLastDisconnectCause == RIL_RESULT_NOCARRIER)
        (void)pRilHandle->BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);
    g_dwLastDisconnectCause = 0;
    return;
}

//
// Windows CE6 only: the normal RIL call state notifications are
// too fast for the Windows CE6 TAPI, so the RIL has to prepare
// them again later
//
// lpParameter [in]: pointer to CHECKDIALSUCCESS_THREAD_DATA structure
//
static DWORD WINAPI CheckDialSuccessThreadProc(LPVOID lpParameter)
{
    CHECKDIALSUCCESS_THREAD_DATA* prtd = (CHECKDIALSUCCESS_THREAD_DATA*)lpParameter;

    // Wait for the dial command to finish by placing a dummy command
    // in the queue.
    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, prtd->pHandle, "AT\r", CMDOPT_LOWESTPRIO, APIID_GETCALLLIST, 0, 0, 0, NULL, NULL, NULL);

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        RILCALLINFO oCallInfo[MAX_NUMBERCALLS];
        INT i;
        DWORD dwID = 0;

        for (i=0; i<g_iNrCallInfo; i++) {
            if (g_CallInfo[i].dwStatus==RIL_CALLSTAT_DIALING || g_CallInfo[i].dwStatus==RIL_CALLSTAT_ALERTING) {
                dwID = g_CallInfo[i].dwID;
                break;
            }
        }
        if (dwID > 0) {
            RILDIALINFO rdi;
            rdi.cbSize = sizeof(rdi);
            rdi.dwParams = RIL_PARAM_DI_ALL;
            rdi.hrCmdId = prtd->hrCmd;
            rdi.dwCallId = dwID;
            (void)(prtd->pHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_DIAL, &rdi, sizeof(RILDIALINFO));
            if (dwID < MAX_NUMBERCALLS) {
                memcpy(oCallInfo, g_CallInfo, sizeof(g_CallInfo));
                oCallInfo[dwID-1].dwParams &= ~RIL_PARAM_CI_STATUS;
                oCallInfo[dwID-1].dwParams |= RIL_PARAM_CI_CPISTATUS;
                oCallInfo[dwID-1].dwStatus = RIL_CPISTAT_NEW_OUTGOING;
                for (i = 0; i < g_iNrCallInfo; i++) {
                    (void)(prtd->pHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_CALLPROGRESSINFO, (LPVOID)&oCallInfo[i], sizeof(RILCALLINFO));
                }
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    delete prtd;
    return 0;
}
#endif

//
// This thread sends a SAT dial command in the background.
//
DWORD WINAPI SendDialSATCommandThreadProc(LPVOID pVoid)
{
    BOOL bNoIconForSAT = FALSE;
    CRilHandle* pHandle = (CRilHandle *)pVoid;
    char szCmd[MAX_ATCMD_LEN];

    if (pHandle == NULL)
        return 1;

    bNoIconForSAT = pHandle->GetSimToolkit()->SATNoIconModus();

    if (INTEL_STYLE_SAT)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+STKTR=16,%u\r", bNoIconForSAT ? 4 : 0);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SSTR=16,%u\r", bNoIconForSAT ? 4 : 0);

    // Since the SSTR command will block the AT interface, the RIL has toi wait for the
    // following audio mode command to be send first.
     pHandle->WaitAudioModeEvent(100);

    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_LOWESTPRIO, APIID_SENDSIMTOOLKITCMDRESPONSE, 0, 0, 0, NULL, ParseATDError, pVoid);
    TestLastActiveCall(pHandle);

    return 0;
}

//
// This ATD command might need to be translated into something else, or
// the number/*#code must be changed.
//
// Function returns TRUE, if the command has been modified.
//
BOOL TranslateATDCmds(char *szCmd, DWORD dwBufferLen)
{
    DWORD dwRegValue = 0;
    char szDial[MAX_ATCMD_LEN];
    BOOL bTranslated = FALSE;

    memset(szDial, 0, MAX_ATCMD_LEN);

    // First check, if there is a valid voice ATD command, then extract number
    if (szCmd == NULL)
        goto Error;
    if (strlen(szCmd) < 6)
        goto Error;
    if (strncmp(szCmd, "ATD", 3) != 0)
        goto Error;
    if (szCmd[strlen(szCmd)-2] != ';' || szCmd[strlen(szCmd)-1] != '\r')
        goto Error;
    (void)strncpyz(szDial, szCmd+3, MAX_ATCMD_LEN);
    szDial[strlen(szDial)-2] = '\0';

    // Do translation of **002 and **004 sequenses into *002 and *004.
    dwRegValue = (DONT_USE_CF_REG_CODE?1:0);        // For HC25 per default "on".
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("TranslateCFCodes"), &dwRegValue);
    if (dwRegValue == 1) {
        // Remove the first '*' in all codes beginninh with **002 or **004.
        if (strncmp(szDial, "**002", 5) == 0 || strncmp(szDial, "**004", 5) == 0) {
            (void)_snprintfz(szCmd, dwBufferLen, "ATD%s;\r", szDial+1);
            bTranslated = TRUE;
        }
    }
Error:
    return bTranslated;
}

//
// If an emergency call is done when there is no network available, the module staya in
// dialing state forever. To Avoid this situation the RIL will abort the dialing attempt when
// after a timeout no audio has been activated.
//
static DWORD WINAPI CheckEmergencyCallSuccessThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;
    DWORD dwStartTime;
    DWORD dwTimeout = DEFAULT_EMERGENCY_DIAL_TIMEOUT;

    dwStartTime = GetTickCount();
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EmergencyCallNoSIMTimeout"), &dwTimeout);

    if (dwTimeout == 0)            // Timeout of 0: timeout is turned off.
        return 0;

    // Unfortunately there are no events available, that could help here. For this
    // reason the RIL must use polling here.
    while (!g_PowerManager.IsRadioOff()) {
        // If module sends the "+CIEV: call,1" URC, call has been successful.
        if (pHandle->GetLastCallURC() == 1)
            break;

        // Emergency call aborted by user?
        if (!g_fEmergencyCallOngoing)
            break;

        // If timeout occurs, abort dial attempt.
        if (GetTickCount() - dwStartTime > dwTimeout) {
            CNotificationData* pnd = NULL;

            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Timeout on dialing emergency call\r\n")));

            // Generate end of emergency call notification
            pnd = new CNotificationData;
            if (pnd) {
                if (!pnd->InitFromRealBlob(RIL_NOTIFY_EMERGENCYHANGUP, NULL, 0)) {
                    delete pnd;
                    pnd = NULL;
                }
            }

            // Deactivate GPS antenna (if necessary)
            if (g_fEmergencyCall) {
                g_fEmergencyCall = FALSE;
                (void)g_GpsHandling.SwitchGpsStatePassive(FALSE);
            }

            // Deactivate audio path
            RHADeactivateVoicePath(pHandle, FALSE, FALSE);

            pHandle->SetVoiceCallDialing(FALSE);

            // Send hangup command
            (void)QueueInternalCmd(HCCMD_URCPORT_ONLY, pHandle, "AT+CHUP\r", CMDOPT_HANGUP, APIID_HANGUP, 0, 0, 0, ParseATHOK, ParseATHError, (LPVOID)pHandle, pnd);
            pnd = NULL;
            break;
        }

        // Sleep a while before poll again.
        Sleep(CALLCTRL_DEFAULT_POLLING_INTERVAL);
    }

    return 0;
}

static HRESULT ParseCdmaDial(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;
    DWORD dwStartTime = GetTickCount();
    BOOL bActive;
    BOOL bVoiceCallDetected;
    BOOL bCallDetectedSoFar;
    INT iIdx;

    bCallDetectedSoFar = FALSE;
    // Problem with voice calls in CDMA: Windows Mobile handles the call states
    // "dialing" and "alerting" like "active", so the phone applications shows
    // immediately "connected". As a solution the RIL must hold back the "OK"
    // from AT+CDV until the call has been connected.
    while (!g_PowerManager.IsRadioOff()) {
        bActive = FALSE;
        bVoiceCallDetected = FALSE;

        // Wait for new call state event
        (void)pHandle->WaitSignalCallStateChange(50);

        EnterCriticalSection(&g_CallInfoSection);
        for (iIdx = 0; iIdx < g_iNrCallInfo; iIdx++) {
            // Check for voice call.
            if (g_CallInfo[iIdx].dwType == RIL_CALLTYPE_VOICE) {
                bVoiceCallDetected = TRUE;
                bCallDetectedSoFar = TRUE;
                // Is call active?
                if (g_CallInfo[iIdx].dwStatus == RIL_CALLSTAT_ACTIVE)
                    bActive = TRUE;
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);

        // I call active now?
        if (bActive) break;
        // Is call aborted? Be aware: it takes a little bit time after AT+CDV
        // for the call to show up, so this check must be performed only, if
        // the voice call has been in the call list at least once.
        if (bCallDetectedSoFar && !bVoiceCallDetected) break;
        // Timeout: call never made it to the call list.
        if (!bCallDetectedSoFar && GetTickCount()-dwStartTime > 1000) break;
    }
    return S_OK;
}

//
//
//
HRESULT RILDrv_Dial(DWORD dwParam, LPCSTR szAddress, DWORD dwType, DWORD dwOptions)
{
#if defined (NODIALING) && !defined (SHIP_BUILD)
    bool fNoDialing;
    bool fNoEmergencyDialing;
#endif // NODIALING
    bool fNoBlackList;
    APIID nAPIID =  APIID_DIAL;
    CNotificationData* pnd = NULL;
    RILCONNECTINFO rci; memset(&rci,0,sizeof(rci)); // zero struct
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    LPSTR szDialStringStart = NULL;
    HRESULT hr = S_OK;
    LPCSTR szAddrWalk;
    BOOL fDataPort = FALSE;
    BOOL bIsEmergencyNo;
    DWORD dwDialOpts = CMDOPT_DIAL|CMDOPT_RETRYONSIMLOCKED;
    LPCSTR szAddressDial;
    BOOL fIsCDMA = FALSE;
    DWORD dwDialDelay = 0;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (szAddress == NULL) {
        hr = E_INVALIDARG;
        goto Error;
    }
    szAddressDial = szAddress;
    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA)
        fIsCDMA = TRUE;

    // When the radio is off, we shouldn't try to dial.
    if (g_PowerManager.IsRadioOff()) {
        hr = RIL_E_RADIOOFF;
        goto Error;
    }

    bIsEmergencyNo = IsEmergencyNumber(szAddress);
    if (bIsEmergencyNo)
        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Dialing Emergency Call\r\n")));

    if (DialAsDefaultEN(szAddress))
        szAddressDial = DEFAULT_EMERGENCY_NUMBER;

    // While in SIM initialization state we shouldn't try to make a call.
    if (!bIsEmergencyNo && !fIsCDMA && (g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) {
        hr = RIL_E_NETWKTEMPFAILURE;
        goto Error;
    }

    // When SIM is not ready, we cannot do anything else than emergency call.
    if (!bIsEmergencyNo && !fIsCDMA && !pHandle->GetDevice()->IsSIMReady()) {
        hr = RIL_E_SIMNOTINSERTED;
        goto Error;
    }

    // CDMA: if the module is not registered, don't make a call attempt (except emergency call).
    if (fIsCDMA && !bIsEmergencyNo && g_dwRegStatus != RIL_REGSTAT_HOME && g_dwRegStatus != RIL_REGSTAT_ROAMING) {
        // Generate the "NO DIALTONE" response.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_RESULT_NODIALTONE))
            hr = E_FAIL;
        goto Error;
    }

    // When SIM is ready, but PIN not entered, we cannot do anything else than emergency call.
    if (!bIsEmergencyNo && !fIsCDMA && g_dwSIMLockedState != RIL_LOCKEDSTATE_READY && g_dwSIMLockedState != RIL_LOCKEDSTATE_UNKNOWN) {
        hr = RIL_E_SIMPINREQUIRED;
        goto Error;
    }

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DelayDialCommand"), &dwDialDelay)) {
        // Delay the dial command.
        // INTERNAL: The reason for this key is, that our dialup-server is too slow to handle
        // INTERNAL: call, which are coming fast after the previous call ended. This delay
        // INTERNAL: gives the server some time.
        Sleep(dwDialDelay);
    }

    // CDMA: in Sprint networks it might be necessary to display an additional warning.
    if (fIsCDMA)
        pHandle->GetDevice()->SprintVoiceCallRoamingWarning();

    g_fEmergencyCall = FALSE;
    (void)g_GpsHandling.SwitchGpsStatePassive(FALSE);
    g_fReleaseHeldCall = FALSE;

    if (SUPPORTS_BLACKLIST && !bIsEmergencyNo && !fIsCDMA) {
        if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoBlackList"), &fNoBlackList) && fNoBlackList) {
            if (USE_CPBS_FOR_BLACKLIST) {
                DWORD dwCurrentPhoneBook = RIL_PBLOC_UNKNOWN;

                // Check currently selected phonebook.
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPBS?\r", CMDOPT_NONE, APIID_GETPHONEBOOKOPTIONS, 0, 0, 0, ParseGetPhonebookOptions, NULL, (LPVOID)&dwCurrentPhoneBook);
                // Select blacklist phonebook and delete only entry.
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CPBS=\"BL\";+CPBW=0\r", CMDOPT_NONE, APIID_NONE, 1000, 0, 0);
                // If current phonebook is know, switch back to it.
                if (dwCurrentPhoneBook != RIL_PBLOC_UNKNOWN) {
                    char szCmdCPBS[MAX_ATCMD_LEN];

                    // Switch back to original phonebook.
                    (void)_snprintfz(szCmdCPBS, MAX_ATCMD_LEN, "AT+CPBS=\"%s\"\r", g_rgszPBLocations[dwCurrentPhoneBook]);
                    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szCmdCPBS, CMDOPT_NONE, APIID_SETPHONEBOOKOPTIONS, 0, 0, 0);
                }
            }
            else
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT^SBLK\r", CMDOPT_NONE, APIID_NONE, 1000, 0, 0);
        }
    }

#if defined (NODIALING) && !defined (SHIP_BUILD)
    // See if we want to disable dialing
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoDialing"), &fNoDialing) && fNoDialing) {
        // We can't allow any dialing (for stress tests)
        hr = E_FAIL;
        goto Error;
    }

    // See if we want to disable emergency dialing
    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoEmergencyDialing"), &fNoEmergencyDialing) &&
        fNoEmergencyDialing) {
        if (bIsEmergencyNo) {
            // We can't allow dialing of emergency numbers (for stress tests)
            hr = E_FAIL;
            goto Error;
        }
    }
#endif // NODIALING

    (void)strncpyz(szCmd, "AT", MAX_ATCMD_LEN);
    szWalk = strchr(szCmd, '\0');  // NO_TYPO: 27

    switch (dwType)
    {
        case RIL_CALLTYPE_VOICE:
			if (SUPPORTS_ONLY_DATAMODE) {
                hr = E_INVALIDARG;
                goto Error;
            }
#ifndef RILDRV_CE6
            if (!fIsCDMA) {
#endif
                // Windows Mobile only: The "CONNECT" notification is not needed in CDMA.
                pnd = new CNotificationData;
                if (pnd) {
                    rci.cbSize = sizeof(RILCONNECTINFO);
                    rci.dwParams = RIL_PARAM_CNI_CALLTYPE;
                    rci.dwCallType = RIL_CALLTYPE_VOICE;
                    if (!pnd->InitFromRealBlob(RIL_NOTIFY_CONNECT, &rci, sizeof(RILCONNECTINFO))) {
                        delete pnd;
                        pnd = NULL;
                    }
                }
#ifndef RILDRV_CE6
            }
#endif

            if (bIsEmergencyNo) SetEmergencyNumber(szAddress);
            if (fIsCDMA)
                (void)strncpyz(szWalk,"+CDV=",MAX_ATCMD_LEN - (szWalk - szCmd));
            else
                (void)strncpyz(szWalk,"D",MAX_ATCMD_LEN - (szWalk - szCmd));
            // Special emergency numbers: some countries do have non-standard emergency numbers
            // (for example Singapore "999"). These numbers are not known to the radio, so we have
            // to use one, which is known. This applies only when the PIN is not entered or no SIM
            // present.
            if (bIsEmergencyNo && !IsEmergencyNumberIntern(szAddress) && g_dwSIMLockedState != RIL_LOCKEDSTATE_READY)
            {
                // Exception: if this number is a ECC number, and the (ECC)-SIM is inserted, the
                // dialed number and not the default emergency number must be used.
                if (!(IsEmergencyNumberECC(szAddress) && g_dwSIMLockedState != RIL_LOCKEDSTATE_UNKNOWN))
                    szAddressDial = DEFAULT_EMERGENCY_NUMBER;
            }

            break;

        case RIL_CALLTYPE_DATA:
            // If the last call had been a fax call, we have to switch back to data.
            if (SUPPORTS_FAX && !fIsCDMA)
                (void)strncpyz(szWalk,"+FCLASS=0;D",MAX_ATCMD_LEN - (szWalk - szCmd));
            else
                (void)strncpyz(szWalk,"D",MAX_ATCMD_LEN - (szWalk - szCmd));
            fDataPort = TRUE;
            // CDMA only: ensure, that the module isn't in data mode. While in
            // GSM mode there might be an active GPRS connection (handled in
            // gprs.cpp), which must be left alone.
            if (fIsCDMA)
                EndCSDDataMode(pHandle->GetDevice());
            break;

        case RIL_CALLTYPE_FAX:
            if (!SUPPORTS_FAX) {
                hr = E_INVALIDARG;
                goto Error;
            }
            (void)strncpyz(szWalk, "+FCLASS=1;D", MAX_ATCMD_LEN - (szWalk - szCmd));
            fDataPort = TRUE;
            break;

        case RIL_CALLTYPE_UNKNOWN:
            hr = E_INVALIDARG;
            goto Error;

        default:
            // This should have been caught in the proxy
            hr = E_INVALIDARG;
            goto Error;
    }

    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    szDialStringStart = szWalk;

    // Copy the number in the dial string
    szAddrWalk = szAddressDial;
    while(*szAddrWalk)
    {
        const char *szAllowedChars = "1234567890*#+ABCD,TPp!W@";

        // Filter out the T and P modifiers in CDMA; they might get
        // misinterpreted by the radio.
        if (fIsCDMA && FILTER_V250_MODIFIERS_IN_CDMA)
            szAllowedChars = "1234567890*#+ABCD,!W@";

        // Only allow characters in the set specified by GSM 07.07 section 6.2
        if (strchr(szAllowedChars,*szAddrWalk))
        {
            *szWalk++ = *szAddrWalk;
        }
        szAddrWalk++;
    }

    pHandle->GetDevice()->SetLastDialedNumber(szAddress);
    // If the dial string is really long, make a note in case the
    // command fails with a generic error. We can then guess that
    // the real error is that the dial string was too long.
    if ((szWalk - szDialStringStart) > MAX_DIAL_STRING_LENGTH)
    {
        dwDialOpts |= CMDOPT_LONGDIALSTRING;
    }

    // Remember to paste on the terminating '\0'
    *szWalk='\0';

    if (!fIsCDMA) {
        if (dwOptions & RIL_DIALOPT_CLOSEDGROUP) {
            (void)strncpyz(szWalk, "G", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk++;
        }

        if (dwOptions & RIL_DIALOPT_RESTRICTID) {
            (void)strncpyz(szWalk, "I", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk++;
        }
        else if (dwOptions & RIL_DIALOPT_PRESENTID) {
            (void)strncpyz(szWalk, "i", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk++;
        }
        // If it's a data call, terminate with a ; character
        if (dwType==RIL_CALLTYPE_VOICE)
        {
            (void)strncpyz(szWalk, ";", MAX_ATCMD_LEN - (szWalk - szCmd));
            szWalk++;
        }
    }

    if (dwType==RIL_CALLTYPE_DATA)
    {
        dwDialOpts |= CMDOPT_DELAYCONNECTRSP;
    }

    (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));

    g_fCallWaiting = FALSE;

    if (dwType == RIL_CALLTYPE_DATA) {
        // If needed (on HC28 per default), disable call waiting for CSD before making
        // a CSD connection to make sure it won't be interrupted by an incoming voice
        // call.
        DWORD dwSetCCWA = 0;

        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableCCWAbeforeCSD"), &dwSetCCWA))
            dwSetCCWA = (DISABLE_CCWA_ON_CSD ? 1 : 0);

        if (dwSetCCWA == 1 && !fIsCDMA)
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CCWA=,0,2\r", CMDOPT_NONE, APIID_SETCALLWAITINGSTATUS, 0, 0, 0, NULL);

        // It might be necessary to change the band setting now.
        EnsureRightCSDBandConfiguration(pHandle->GetDevice());

#ifdef RIL_ALLOW_TRACE_PPP_DATA
        g_ComManager.TraceExtData(TRUE, TRUE);
#endif
    }

    if (fDataPort == TRUE) {
        if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, szCmd, dwDialOpts | CMDOPT_DIALDATA, APIID_DIAL, NULL, pnd, hr, ParseATDCSDError, (LPVOID)pHandle->GetDevice())) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        PFN_CMD_PARSE pfnParse = NULL;
#ifdef RILDRV_CE6
        CHECKDIALSUCCESS_THREAD_DATA *pData = NULL;
#endif
        BOOL bActiveCall = FALSE;
        BOOL bNoIconForSAT = FALSE;
        BOOL bDoNoop = FALSE;

        // Unlike other modules like the MC75 or MC55 the HC25 can only make a new voice call,
        // if existing calls are all on hold. So if there is an active call we have put
        // this call on hold manually.
        if (!SUPPORTS_SLCC || g_iNrCallInfo < 0 || AVOID_SLCC)
            (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle->GetDevice(), "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListForceCheck, NULL, (LPVOID)(pHandle->GetDevice()));
        EnterCriticalSection(&g_CallInfoSection);
        if (g_iNrCallInfo > 0) {
            INT i;

            for (i=0; i<g_iNrCallInfo; i++) {
                if (g_CallInfo[i].dwStatus==RIL_CALLSTAT_ACTIVE) {
                    bActiveCall = TRUE;
                    break;
                }
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);
        if (bActiveCall && !fIsCDMA)
            (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle->GetDevice(), "AT+CHLD=2\r", CMDOPT_NONE, APIID_MANAGECALLS, 0, 0, 0, NULL);

        g_LastATD = GetTickCount();
        g_bNeedCallInfoRecheck = TRUE;

        if (dwType == RIL_CALLTYPE_VOICE && pHandle->GetDevice()->GetSimToolkit()->SATOverrideNeeded(szAddress, bNoIconForSAT)) {
            // If there is a SAT setup call active, this command must be handled.
            if (INTEL_STYLE_SAT)
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+STKTR=16,%d\r", bNoIconForSAT ? 4 : 0);
            else
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SSTR=16,%d\r", bNoIconForSAT ? 4 : 0);
            szWalk = strchr(szCmd, '\0');
            nAPIID = APIID_SENDSIMTOOLKITCMDRESPONSE;
            g_fSATCallActive = TRUE;
            bDoNoop = SSTR16_IS_BLOCKING;
        }
        else {
            (void)TranslateATDCmds(szCmd, MAX_ATCMD_LEN);
        }
        if (bDoNoop) {
            if (!QueueCmdWithTimeout(HCCMD_NOOP_PORT, pHandle, NULL, NULL, dwDialOpts|CMDOPT_NOOP, nAPIID, NULL, pnd, hr, 0, 0, 0, NULL, (LPVOID)pHandle->GetDevice(), NULL, NULL)) {
                hr = E_FAIL;
                goto Error;
            }
            CloseHandle(CreateThread(NULL, 0, SendDialSATCommandThreadProc, (LPVOID)pHandle->GetDevice(), 0, 0));
        }
        else {
            if (fIsCDMA) {
                DWORD dwHoldResponse = 0;

                // Optional: hold result from dial command until the call is estabished,
                // rejected by called party or aborted by network. This solves the
                // problem, that the Windows Mobile dialer shows "connected", even
                // when the call is still dialing. On the other hand the call cannot be
                // aborted by the user.
                (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("HoldCdmaDialResult"), &dwHoldResponse);
                if (dwHoldResponse == 1)
                    pfnParse = ParseCdmaDial;
            }
            if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, dwDialOpts, nAPIID, pfnParse, pnd, hr, 0, 0, 0, ParseATDError, (LPVOID)pHandle->GetDevice(), NULL, PreInitDialing)) {
                TestLastActiveCall(pHandle->GetDevice());
                hr = E_FAIL;
                goto Error;
            }
            // This is an emergency call, send the special notification.
            if (bIsEmergencyNo) {
                (void)pHandle->GetDevice()->BroadcastRealBlobNotification(RIL_NOTIFY_DEVSPECIFIC_DIAL_EMERGENCY_CALL, NULL, 0);
                g_fEmergencyCallOngoing = TRUE;
                // Problem: if the emergency call is done when there is no network availabe, the module
                // might stay in the dialing state for ever. To avoid this, a thread will be started, that
                // abaorts dialing if after a timeout no connection has been established.
                if (ENDLESS_DIAL_ON_EMERGENCYCALL)
                    CloseHandle(CreateThread(NULL, 0, CheckEmergencyCallSuccessThreadProc, (LPVOID)pHandle->GetDevice(), 0, 0));
            }
        }
#ifdef RILDRV_CE6
        pData = new CHECKDIALSUCCESS_THREAD_DATA;
        if (pData != NULL) {
            pData->hrCmd = hr;
            pData->pHandle = pHandle->GetDevice();
            CloseHandle(CreateThread(NULL, 0, CheckDialSuccessThreadProc, (LPVOID)pData, 0, 0));
        }
#endif
    }

    pnd = NULL;

Error:
    return hr;
}

//
//
//
#ifdef RILDRV_CE6
static HRESULT ParseATAOK(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;

    if (s_nCDMAAnswerIdx > 0) {
        RILCALLINFO oCallInfo;

        memset(&oCallInfo, 0, sizeof(RILCALLINFO));
        oCallInfo.cbSize = sizeof(RILCALLINFO);
        oCallInfo.raAddress.cbSize = sizeof(RILADDRESS);
        oCallInfo.dwParams = RIL_PARAM_CI_ID|RIL_PARAM_CI_DIRECTION|RIL_PARAM_CI_CPISTATUS|RIL_PARAM_CI_TYPE|RIL_PARAM_CI_MULTIPARTY;
        oCallInfo.dwID = s_nCDMAAnswerIdx;
        oCallInfo.dwDirection = RIL_CALLDIR_INCOMING;
        oCallInfo.dwStatus = RIL_CPISTAT_CONNECTED;
        oCallInfo.dwType = RIL_CALLTYPE_VOICE;
        oCallInfo.dwMultiparty = RIL_CALL_SINGLEPARTY;
        (void)pHandle->BroadcastRealBlobNotification(RIL_NOTIFY_CALLPROGRESSINFO, (LPVOID)&oCallInfo, sizeof(RILCALLINFO));
        s_nCDMAAnswerIdx = 0;
    }
    return S_OK;
}

static HRESULT ParseATAError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/,LPVOID /*pParam*/, DWORD& /*dwCode*/)
{
    s_nCDMAAnswerIdx = 0;
    return S_OK;
}
#endif

//
//
//
HRESULT RILDrv_Answer(DWORD dwParam)
{
    CNotificationData* pnd = NULL;
    RILCONNECTINFO rci; memset(&rci,0,sizeof(rci)); // zero struct
    HRESULT hr = S_OK;
    BOOL bCallActive = FALSE;
    BOOL bIsCSDCall = FALSE;
    BOOL bIsFaxCall = FALSE;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (g_fEmergencyCall) {
        g_fEmergencyCall = FALSE;
        (void)g_GpsHandling.SwitchGpsStatePassive(FALSE);
    }

    g_fReleaseHeldCall = FALSE;

    if (!SUPPORTS_SLCC || g_iNrCallInfo < 0 || AVOID_SLCC)
        (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle->GetDevice(), "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListForceCheck, NULL, (LPVOID)(pHandle->GetDevice()));
    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        INT i;

        for (i=0; i<g_iNrCallInfo; i++) {
            if (g_CallInfo[i].dwStatus==RIL_CALLSTAT_INCOMING) {
                if (g_CallInfo[i].dwType == RIL_CALLTYPE_DATA)
                    bIsCSDCall = TRUE;
                if (g_CallInfo[i].dwType == RIL_CALLTYPE_FAX)
                    bIsFaxCall = TRUE;
            }
            if (g_CallInfo[i].dwStatus==RIL_CALLSTAT_ACTIVE || g_CallInfo[i].dwStatus==RIL_CALLSTAT_ONHOLD) {
                bCallActive = TRUE;
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    if (bIsCSDCall || bIsFaxCall)
    {
        // If data port is already in use we cannot accept any CSD or Fax call.
        if (pHandle->GetDevice()->GetComDevice(DATA_PORT)->FDataMode())
        {
            hr = E_FAIL;
            goto Error;
        }

        // Set correct +FCLASS for incoming CSD/Fax call.
        if (bIsCSDCall)
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+FCLASS=0\r", CMDOPT_NONE, APIID_SETBEARERSERVICEOPTIONS, 0, 0, 0);
        if (bIsFaxCall)
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+FCLASS=1\r", CMDOPT_NONE, APIID_SETBEARERSERVICEOPTIONS, 0, 0, 0);

        g_dwLastVoiceCmd = GetTickCount();
        if (!QueueCmd(HCCMD_DATAPORT_ONLY, pHandle, "ATA\r", CMDOPT_ANSWER|CMDOPT_DIALDATA, APIID_ANSWER, NULL, pnd, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        // In CDMA there is no need to distinguish between active call or no active call.
        if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA)
            bCallActive = FALSE;

        // Set up the voice connect notification. If the incoming call is a data call,
        // we will get a "CONNECT" instead of "OK", which will prevent this notification
        // from being sent.
        pnd = new CNotificationData;
        if (pnd) {
            rci.cbSize = sizeof(RILCONNECTINFO);
            rci.dwParams = RIL_PARAM_CNI_CALLTYPE;
            rci.dwCallType = RIL_CALLTYPE_VOICE;
            if (!pnd->InitFromRealBlob(RIL_NOTIFY_CONNECT, &rci, sizeof(RILCONNECTINFO))) {
                delete pnd;
                pnd = NULL;
            }
        }

        g_dwLastVoiceCmd = GetTickCount();
        pHandle->GetDevice()->SetVoiceCallDialing(FALSE);
#ifdef RILDRV_CE6
        if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA)
            s_nCDMAAnswerIdx = 1;
        else
            s_nCDMAAnswerIdx = 0;
        if (!QueueCmdWithTimeout(HCCMD_URCPORT_ONLY, pHandle, bCallActive ? "AT+CHLD=2\r" : "ATA\r", NULL, CMDOPT_ANSWER, APIID_ANSWER, ParseATAOK, pnd, hr, 0, 0, 0, ParseATAError, (LPVOID)(pHandle->GetDevice()), NULL, PreInitAnswer)) {
            hr = E_FAIL;
            goto Error;
        }
#else
        if (!QueueCmdWithTimeout(HCCMD_URCPORT_ONLY, pHandle, bCallActive ? "AT+CHLD=2\r" : "ATA\r", NULL, CMDOPT_ANSWER, APIID_ANSWER, NULL, pnd, hr, 0, 0, 0, NULL, NULL, NULL, PreInitAnswer)) {
            hr = E_FAIL;
            goto Error;
        }
#endif
    }
    pnd = NULL;

Error:
    return hr;
}

//
//
//
void RHADeactivateVoicePath(CRilHandle *pRILHandle, BOOL bAcceptIntermediateState, BOOL bDoCheck)
{
    BOOL bCallActive = FALSE;
    BOOL bCanDeactivateVoicePath = TRUE;

    if (g_fEmergencyCall)
        bCallActive = TRUE;

    if (bDoCheck)
        (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pRILHandle, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListCheck, NULL, (LPVOID)pRILHandle);

    if (g_iNrCallInfo > 0 && !g_fEmergencyCall) {
        EnterCriticalSection(&g_CallInfoSection);
        if (g_iNrCallInfo > 0) {
            INT i;

            for (i=0; i<g_iNrCallInfo; i++) {
                if (g_CallInfo[i].dwType != RIL_CALLTYPE_DATA && g_CallInfo[i].dwType != RIL_CALLTYPE_FAX && (g_CallInfo[i].dwStatus==RIL_CALLSTAT_ACTIVE || g_CallInfo[i].dwStatus==RIL_CALLSTAT_ONHOLD)) {
                    bCallActive = TRUE;
                    break;
                }
                if (bAcceptIntermediateState && g_CallInfo[i].dwType != RIL_CALLTYPE_DATA && g_CallInfo[i].dwType != RIL_CALLTYPE_FAX && (g_CallInfo[i].dwStatus==RIL_CALLSTAT_INCOMING || g_CallInfo[i].dwStatus==RIL_CALLSTAT_ALERTING || g_CallInfo[i].dwStatus==RIL_CALLSTAT_DIALING)) {
                    bCallActive = TRUE;
                    break;
                }
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);
    }

    if (pRILHandle != NULL)
    {
        if (!bCallActive)
            pRILHandle->SetSLCCaudioActive(FALSE);
        bCanDeactivateVoicePath = (!pRILHandle->IsSLCCAudioActive() && !pRILHandle->IsCIEVAudioActive());
    }

    if(g_fAudioPathActivated && !bCallActive && bCanDeactivateVoicePath)
    {
        (void)RHA_DeactivateVoicePath();
        StopNoUsbSuspendThread();
        if (g_fEmergencyCallOngoing && pRILHandle != NULL)
            (void)pRILHandle->BroadcastRealBlobNotification(RIL_NOTIFY_DEVSPECIFIC_HANGUP_EMERGENCY_CALL, NULL, 0);
        g_fEmergencyCallOngoing = FALSE;
        g_fAudioPathActivated = FALSE;
        g_fNoMuteCmdSoFar = TRUE;
        g_dwAudioSelection = 0;
        g_fSATCallActive = FALSE;
    }
}

//
//
//
void TestLastActiveCall(CRilHandle* const pRilDevice)
{
	if(SUPPORTS_ONLY_DATAMODE){
		return;
	}
	
	RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: AT+CLCC D\r\n")));
    (void)QueueInternalCmd(HCCMD_STANDARD, pRilDevice, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallList, NULL, pRilDevice);
}

DWORD WINAPI ReCheckCallStatusThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    // Sleep a little bit to allow other notification which may be pending to be sent.
   Sleep(100);

	if(SUPPORTS_ONLY_DATAMODE){
		return 0;
	}

    // Check the current call status and notifiy the MMI if actual call status differs from the chached status.
    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListCheck, NULL, pVoid);

    return 0;
}


DWORD WINAPI RejectVCallThreadProc(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    // Incoming video calls must be rejected with the release cause 88.
    (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT^SHUP=88\r", CMDOPT_NONE, APIID_MANAGECALLS, 0, 0, 0, NULL, NULL, pVoid);

    return 0;
}

//
//
//
static HRESULT ParseATHError(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/,LPVOID pParam, DWORD& /*dwCode*/)
{
    // Due to odd circumstances  it might happen, that we either miss an URC with a call status change or
    // the module didn't send one. In this case we start a thread to re-check the current call status.´
    CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, pParam, 0, 0));
#ifdef RILDRV_CE6
    s_nCDMAHangupIdx = 0;
#endif
    return S_OK;
}

//
//
//
#ifdef RILDRV_CE6
static HRESULT ParseCHLDOK(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;

    // Testcase "2-Way Call" of Windows CE7 requires the call state notification
    // to be sent before the OK message for the AT+CHLD command.
    (void)pHandle->WaitSignalCallStateChange(WAIT_FOR_SLCC_AFTER_OK);
    return S_OK;
}
#endif

//
//
//
static HRESULT ParseATHOK(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;

    if (g_fEmergencyCall) {
        // In case hanging up of an emegergency call, the GPS can now be swithed off.
        g_fEmergencyCall = FALSE;
        (void)g_GpsHandling.SwitchGpsStatePassive(FALSE);
    }

#ifdef RILDRV_CE6
    if (g_iLastIdxDisconnected > 0)
        SendDisconnectNotification(pHandle, RIL_RESULT_CALLABORTED);
    else if (AVOID_SLCC)
        g_dwLastDisconnectCause = RIL_RESULT_CALLABORTED;
    if (s_nCDMAHangupIdx > 0) {
        // End of CDMA data call: generate end of call notification now, since
        // the module won't send a ^SLCC: URC.
        RILCALLINFO oCallInfo;

        memset(&oCallInfo, 0, sizeof(RILCALLINFO));
        oCallInfo.cbSize = sizeof(RILCALLINFO);
        oCallInfo.raAddress.cbSize = sizeof(RILADDRESS);
        oCallInfo.dwParams = RIL_PARAM_CI_ID|RIL_PARAM_CI_DIRECTION|RIL_PARAM_CI_CPISTATUS|RIL_PARAM_CI_TYPE|RIL_PARAM_CI_MULTIPARTY|RIL_PARAM_CI_ADDRESS;
        oCallInfo.dwID = s_nCDMAHangupIdx;
        oCallInfo.dwDirection = RIL_CALLDIR_OUTGOING;
        oCallInfo.dwStatus = RIL_CPISTAT_DISCONNECTED;
        oCallInfo.dwType = (s_nCDMAHangupIdx == 9 ? RIL_CALLTYPE_DATA : RIL_CALLTYPE_VOICE);
        oCallInfo.dwMultiparty = RIL_CALL_SINGLEPARTY;
        if (strlen(pHandle->GetLastDialedNumber()) > 0)
            (void)StringToRILAddress(pHandle->GetLastDialedNumber(), 128, oCallInfo.raAddress);
        else if (s_nCDMAHangupIdx == 9)
            (void)StringToRILAddress(CDMA_DATACONNECTION, 128, oCallInfo.raAddress);
        else
            oCallInfo.dwParams &= ~RIL_PARAM_CI_ADDRESS;
        (void)pHandle->BroadcastRealBlobNotification(RIL_NOTIFY_CALLPROGRESSINFO, (LPVOID)&oCallInfo, sizeof(RILCALLINFO));
        s_nCDMAHangupIdx = 0;
    }

    (void)pHandle->WaitSignalCallStateChange(WAIT_FOR_SLCC_AFTER_OK);
#endif
    // Workaround for bug in some PXS8 firmware versions: the final ^SLCC: is not
    // sent by the radio, so the RIL must actively check the new status.
    if (MISSING_SLCC_AT_CALLEND)
        CloseHandle(CreateThread(NULL, 0, ReCheckCallStatusThreadProc, (LPVOID)pHandle, 0, 0));
    return S_OK;
}

//
// GetHangupATCommand()
// There are 4 different commands to hangup a call, depending on current
// used tecnology and type of call (voice/data). If it is necessary to send
// this command on a special port, the function will set pPort.
//
// Return value: appropiate hangup command including <CR>
//
static const char *GetHangupATCommand(CRilHandle *pHandle, HCCMD_MODE *pPort)
{
    BOOL fIsCDMA = FALSE;
    BOOL fIsData = FALSE;
    BOOL fHeldWaitingCall = FALSE;
    INT  nIdxToHangup = 0;
    const char *cszCmd;

#ifdef RILDRV_CE6
    s_nCDMAHangupIdx = 0;
#endif
    if (pHandle->GetCurrentTechnology() == TEC_MODE_CDMA)
        fIsCDMA = TRUE;

    // No need to do any further checks in case of an emergency call.
    if (!g_fEmergencyCall) {
        // The current call list might not be up to date in this moment.
        if (!SUPPORTS_SLCC || g_iNrCallInfo < 0 || AVOID_SLCC)
            (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pHandle, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListForceCheck, NULL, (LPVOID)pHandle);
        // Step through list of current call to find out, if this is a data
        // or a voice call.
        EnterCriticalSection(&g_CallInfoSection);
        if (g_iNrCallInfo > 0) {
            INT i;
            DWORD dwDataIdx = 0;
            DWORD dwVoiceIdx = 0;

            // Step throuh active calls to find out if there
            // are voice / data calls active.
            for (i=0; i<g_iNrCallInfo; i++) {
                if (g_CallInfo[i].dwType == RIL_CALLTYPE_DATA || g_CallInfo[i].dwType == RIL_CALLTYPE_FAX)
                    dwDataIdx = g_CallInfo[i].dwID;
                else
                    dwVoiceIdx = g_CallInfo[i].dwID;
                if (g_CallInfo[i].dwStatus == RIL_CALLSTAT_ONHOLD || g_CallInfo[i].dwStatus == RIL_CALLSTAT_WAITING)
                    fHeldWaitingCall = TRUE;
            }
            // Hangup voice call per default
            nIdxToHangup = dwVoiceIdx;
            // If there is a data call and no voice call, hangup applies
            // to this call (only GSM/UMTS).
            if (dwDataIdx > 0) {
                fIsData = TRUE;
                nIdxToHangup = dwDataIdx;
            }
            // CDMA: if there is a voice call, hangup applies to  this call,
            // even if there is a data call.
            if (fIsCDMA && dwVoiceIdx > 0) {
                fIsData = FALSE;
                nIdxToHangup = dwVoiceIdx;
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);
        if (fIsCDMA) {
            // In CDMA the calls are not included in the call list. So empty list means: data call.
            if (g_iNrCallInfo <= 0) {
                fIsData = TRUE;
#ifdef RILDRV_CE6
                nIdxToHangup = 9;
#endif
            }
        }
    }

    if (!fIsData || (fIsCDMA && g_iNrCallInfo > 1)) {
        // Hangup voice call needs different command for CDMA and GSM.
        if (fIsCDMA)
            cszCmd = "AT+CHV\r";
        else if (CHUP_FOR_ACTIVE_CALLS_ONLY && fHeldWaitingCall)
            cszCmd = "AT^SHUP=16,0\r";
        else
            cszCmd = "AT+CHUP\r";
    }
    else {
        // Data calls are ended with ATH in CDMA and GSM, but in GSM it might
        // be necessary to use AT^SHUP depending of the module type.
        if (REPLACE_ATH_WITH_SHUP && !fIsCDMA)
            cszCmd = "AT^SHUP=16,0\r";
        else {
            cszCmd = "ATH\r";

            if (MUST_SEND_ATH_ON_DATA_PORT) {
                // End data mode, if still active.
                EndCSDDataMode(pHandle);

                // Specify the port to use to "data port only".
                if (*pPort != NULL) *pPort = HCCMD_DATAPORT_ONLY;
            }
        }
    }
#ifdef RILDRV_CE6
    // Very strange: for CDMA calls the TAPI wants the call progress notification
    // to be sent before the OK. For GSM it is OK to sent it afterwards.
    if (fIsCDMA)
        s_nCDMAHangupIdx = nIdxToHangup < 0 ? 9 : nIdxToHangup;
#endif
    return cszCmd;
}

//
//
//
HRESULT RILDrv_Hangup(DWORD dwParam)
{
    HCCMD_MODE nPort = HCCMD_URCPORT_ONLY;
    const char *cszCmd = NULL;
    CNotificationData* pnd = NULL;
    BOOL fSuccess;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    g_fReleaseHeldCall = FALSE;

    // Generate disconnect notification.
    pnd = new CNotificationData;
    if (pnd) {
        if (pHandle->FPreferred()) {
            fSuccess = pnd->InitFromRealBlob(RIL_NOTIFY_EMERGENCYHANGUP, NULL, 0);
        } else {
            fSuccess = pnd->InitFromDWORDBlob(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_LOCAL);
        }
        if (!fSuccess) {
            // Delete notifiation if it couldn't be initialized.
            delete pnd;
            pnd = NULL;
        }
    }

    g_fCallWaiting = FALSE;
    pHandle->GetDevice()->SetVoiceCallDialing(FALSE);
    g_dwLastVoiceCmd = GetTickCount();
    cszCmd = GetHangupATCommand(pHandle->GetDevice(), &nPort);

    // Commands on data port apply to data connections, so there is no reason
    // to work on the voice path.
    if (nPort != HCCMD_DATAPORT_ONLY)
        RHADeactivateVoicePath(pHandle->GetDevice(), FALSE, FALSE);

    if (!QueueCmd(nPort, pHandle, cszCmd, CMDOPT_HANGUP, APIID_HANGUP, ParseATHOK, pnd, hr, ParseATHError, (LPVOID)(pHandle->GetDevice()))) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}

static HRESULT ParseVTSError(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob,LPVOID pParam, DWORD& dwCode)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;

    if (pHandle != NULL)
        pHandle->StopTemporaryMuting();

    if (WRONG_ERROR_ON_VTS) {
        // This error message is wrong. Actually the command succeeded, so
        // the RIL must return "OK" here.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
        // The error message comes immediately, so we have to wait here for the
        // time the command would take, if it worked correctly.
        Sleep(s_dwDuration);
    }

    return S_OK;
}

static HRESULT ParseVTSOK(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID pParam)
{
    CRilHandle *pHandle = (CRilHandle *)pParam;

    if (pHandle != NULL)
        pHandle->StopTemporaryMuting();

    return S_OK;
}

//
//
//
HRESULT RILDrv_SendDTMF(DWORD dwParam, LPCSTR lpszChars, DWORD dwDuration)
{
    UINT NumVals = 0;
    UINT nValue = 0;
    DWORD dwTimeout = 0;
    DWORD dwDisableVTS = 0;
    char szCmd[MAX_ATCMD_LEN];
    LPSTR szWalk = szCmd;
    LPCSTR pchChars;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if ((dwDuration < 300) || (dwDuration == RIL_DTMFDURATION_DEFAULT))
    {
        dwDuration = 300;
    }
    // Store duration for VTS-Error-Workaround.
    s_dwDuration = dwDuration;

    // Problem with Microsoft MMI: when givin call control commands via key pad (like <1><1><Send>), the
    // MMI first sends the numbers as DTMF tones and than executes the manage call command. This doesn't
    // work well during GCF tests, so DTMF can be deactivated via registry.
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DisableDTMF"), &dwDisableVTS);

    if (dwDisableVTS == 1) {
        // If VTS is disabled, simple return OK without executing any command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_SENDDTMF, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        nValue = (dwDuration + 50 ) / 100;

        pchChars = lpszChars;
        while (*pchChars) {
            hr = S_OK;

            (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "AT");
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            if (SUPPORTS_VTD_COMMAND) {
                (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+VTD=%d;", nValue);
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            }

            while (*pchChars && strlen(szCmd) < (MAX_ATCMD_LEN-(MAX_DTMF_TONES_PER_CMD+20)))
            {
                if (SUPPORTS_VTD_COMMAND)
                    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+VTS=");
                else
                    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "+VTS=\"");
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

                NumVals=0;
                for (; *pchChars && NumVals < MAX_DTMF_TONES_PER_CMD; pchChars++) {
                    NumVals++;
                    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "%c", *pchChars);
                    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
                }
                if (SUPPORTS_VTD_COMMAND)
                    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), ";", nValue);
                else
                    (void)_snprintfz(szWalk, MAX_ATCMD_LEN - (szWalk - szCmd), "\",%d;", nValue);
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

                // Workaround: the module might return "ERROR" even when the
                // DTMF tone has been sent. In that case the AT concatination
                // doesn't work and the RIL has to send eacht tone with one
                // AT command line.
                if (WRONG_ERROR_ON_VTS)
                    break;
            }

            szWalk--;  // delete the unnecessary ;
            (void)strncpyz(szWalk, "\r", MAX_ATCMD_LEN - (szWalk - szCmd));  // NO_TYPO: 30

            // Pick a timeout value to use
            dwTimeout = (2 * NumVals * dwDuration) + g_TimeoutAPIDefault;

            pHandle->GetDevice()->StartTemporaryMuting();
            if (*pchChars) {
                // If there are still tone to play left, make only an internal call.
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_SUPPRESSLOGGING, APIID_SENDDTMF, 0, 0, 0, ParseVTSOK, ParseVTSError, (LPVOID)pHandle->GetDevice());
            }
            else {
                // No more tones to play, now make the actual function call.
                if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_SUPPRESSLOGGING, APIID_SENDDTMF, ParseVTSOK, NULL, hr, dwTimeout, 0, 0, ParseVTSError, (LPVOID)pHandle->GetDevice())) {
                    pHandle->GetDevice()->StopTemporaryMuting();
                    hr = E_FAIL;
                    goto Error;
                }
            }
            szWalk = szCmd;
        }
    }

Error:
    return hr;
}

//
// CheckCDMADataConnStatus()
// In CDMA data connections are expected to show up in the list of active calls.
// In case the CDAM connection is dormant the data call won't be shown by the
// module, so the RIL must add it to the list of active calls.
//
// Parameters:
// pDevice [in]: handle to CRilHAndle
// pCurrent [in]: list of calls reported by the module
// nNum [in]: number of calls in pCurrent
// pAddInfo [out]: if the data call nedds to be addedm this is the entry to use
//
// Return value: TRUE, if data call (pAddInfo) has to be added to the list.
//
BOOL CheckCDMADataConnStatus(CRilHandle *pDevice, RILCALLINFO *pCurrent, INT nNum, RILCALLINFO *pAddInfo)
{
    INT i;

    // In GSM environment this function might get called without RIL handle.
    if (pDevice == NULL) return FALSE;
    // If the mdouel is not connected to CDMA this check doesn't apply.
    if (pDevice->GetCurrentTechnology() != TEC_MODE_CDMA) return FALSE;
    // If the module is not in data mode, there is no reason to check this.
    if (!pDevice->GetComDevice(DATA_PORT)->FDataMode()) return FALSE;

    // Check for a call with ID 9: if it is present, the data call is already listed.
    for (i = 0; i < nNum; i++) {
        if (pCurrent[i].dwID == 9) return FALSE;
    }

    // Generate a fake call status for the CDMA data call.
    memset(pAddInfo, 0, sizeof(RILCALLINFO));
    pAddInfo->cbSize = sizeof(RILCALLINFO);
    pAddInfo->raAddress.cbSize = sizeof(RILADDRESS);
    pAddInfo->dwParams = RIL_PARAM_CI_ID|RIL_PARAM_CI_DIRECTION|RIL_PARAM_CI_STATUS|RIL_PARAM_CI_TYPE|RIL_PARAM_CI_MULTIPARTY|RIL_PARAM_CI_ADDRESS;
    pAddInfo->dwID = 9;
    pAddInfo->dwDirection = RIL_CALLDIR_OUTGOING;
    pAddInfo->dwStatus = RIL_CALLSTAT_ACTIVE;
    pAddInfo->dwType = RIL_CALLTYPE_DATA;
    pAddInfo->dwMultiparty = RIL_CALL_SINGLEPARTY;
    (void)StringToRILAddress(CDMA_DATACONNECTION, 128, pAddInfo->raAddress);
    return TRUE;
}

//
//
//
static HRESULT ParseGetCallListInternal(INT iDoCheck, LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nNoCalls = 0;
    UINT nNoVoiceCalls = 0;
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    char szAddress[MAXLENGTH_ADDRESS];
    RILCALLINFO* rgrci = NULL;
    LPCSTR szDummy;
    HRESULT hr = S_OK;
    BOOL bSuccess;
    BOOL bIncomingCall = FALSE;
    BOOL bContinue = FALSE;
    BOOL bIsNewFirstCall = FALSE;
    INT iCallInfoOrg;
    CRilHandle *pDevice;
    RILCALLINFO oAddInfo;
    RILCALLINFO oCallInfo[MAX_NUMBERCALLS];
    INT iNrCallInfo;
    BOOL fIsCDMA = FALSE;

    pDevice = (CRilHandle *)pParam;

    if (pDevice->GetCurrentTechnology() == TEC_MODE_CDMA)
        fIsCDMA = TRUE;

    pBlob = NULL;
    cbBlob = 0;
    if (AVOID_SLCC && EMPTY_LINE_CLCC_INDICATES_ERROR) {
        // Bug in PH8 Rel2: sometimes the AT+CLCC command can temporarily not get
        // the current call list. Unfortunately it returns "OK" in this case. This
        // situation can be detected by counting the empty lines. If there are three
        // empty lines and no +CLCC, the AT+CLCC is temporarily out of order.
        if (strstr(szRsp, "\r\n\r\n\r\n") != NULL && strstr(szRsp, "+CLCC:") == NULL) {
            hr = RIL_E_NETWKTEMPFAILURE;
            goto Error;
        }
    }

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

    EnterCriticalSection(&g_CallInfoSection);
    iNrCallInfo = g_iNrCallInfo;
    memset(oCallInfo, 0, MAX_NUMBERCALLS * sizeof(RILCALLINFO));
    if (iNrCallInfo > 0) memcpy(oCallInfo, g_CallInfo, iNrCallInfo * sizeof(RILCALLINFO));
    iCallInfoOrg = g_iNrCallInfo;
    if (g_iNrCallInfo == 0)
        bIsNewFirstCall = TRUE;
    // The RIL generates a preminary call progress notification when the
    // +CRING: URC appears the 1st time. This notification always use the
    // ID 1, but the now following AT+CLCC might use a differnt ID, which
    // might be needed to be corrected.
    if (g_iNrCallInfo == 1 && g_CallInfo[0].dwStatus == RIL_CALLSTAT_INCOMING)
        bIsNewFirstCall = TRUE;
    LeaveCriticalSection(&g_CallInfoSection);

    // Check for bug with missing second quotation mark and <CR><LF>
    if (WRONG_CDMA_CLCC_MISSING_EOL && fIsCDMA) {
        const char *cszWrongResponse = "\"+CLCC:";

        // If the problems has been detected: skip the incorrect first line completely.
        // The skipd line holds the status of the data call, this missing status
        // will be added later in the function CheckCDMADataConnStatus().
        if (MatchStringAnywhere(szRsp, cszWrongResponse, szRsp)) {
            // Skip back to the "+CLCC:".
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Skipping incomplete status line in AT+CLCC response.\r\n")));
            szRsp -= strlen(cszWrongResponse) -1;
        }
    }

    // Parse "+CLCC: "
    while (MatchStringBeginning(szRsp, "+CLCC: ", szRsp)) {
        BOOL fNegative = FALSE;
        BOOL fSetToIncoming = FALSE;

        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrci, sizeof(RILCALLINFO), nUsed, &nAllocated, CALLCTRL_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        nNoCalls ++;

        memset(&rgrci[nUsed], 0x00, sizeof(RILCALLINFO));
        rgrci[nUsed].cbSize = sizeof(RILCALLINFO);
        rgrci[nUsed].raAddress.cbSize = sizeof(RILADDRESS);

        // Problem with PXS8 CDMA data calls: the call index might be negativ.
        if (MatchStringBeginning(szRsp, "-", szRsp))
            fNegative = TRUE;

        // Parse "<id>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        // Negativ index: count down from 10, so there will be no conflict
        // with voice calls (positive index).
        if (fNegative && nValue < 10)
            nValue = 10 - nValue;
        fSetToIncoming = FALSE;
        if (WRONG_CLCC_IDX_10_IN_CDMA && nValue == 10 && pDevice != NULL) {
            // Bug in PXS8 in CDMA mode: <idx>=10 is an illegal value, the correct
            // value is 1. If this is the case also the <dir> parameter is wrong. It
            // is supposed to be "incoming" in this case.
            if (fIsCDMA) {
                nValue = 1;
                fSetToIncoming = TRUE;
            }
        }
        rgrci[nUsed].dwID = nValue;
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_ID;

        // Parse ",<direction>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
            goto Continue;
        }
        // The <direction> parameter might be wrong (see "wrong CLCC index 10"
        // workaround above).
        if (fSetToIncoming)
            nValue = 1;
        rgrci[nUsed].dwDirection = (nValue ? RIL_CALLDIR_INCOMING : RIL_CALLDIR_OUTGOING);
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_DIRECTION;

        // Parse ",<status>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_CALLSTATS, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwStatus = g_rgdwCallStats[nValue];
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_STATUS;

        switch (g_rgdwCallStats[nValue])
        {
        case RIL_CALLSTAT_INCOMING:
        case RIL_CALLSTAT_WAITING:
            bIncomingCall = TRUE;
            break;
        default:
            break;
        }

        // Parse ",<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, NUM_CALLTYPES, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwType = g_rgdwCallTypes[nValue];
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_TYPE;
        if (rgrci[nUsed].dwType == RIL_CALLTYPE_VOICE)
            nNoVoiceCalls++;

        if (WRONG_CDMA_CLCC_DATA_CALL_STATUS && fIsCDMA && rgrci[nUsed].dwType == RIL_CALLTYPE_DATA) {
            // Bug in PXS8: the AT+CLCC command somtimes reports an active data
            // connection as "dialing".
            if (rgrci[nUsed].dwStatus == RIL_CALLSTAT_DIALING && pDevice->GetComDevice(DATA_PORT)->FDataMode()) {
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Replacing wrong dialing status for data call in AT+CLCC response.\r\n")));
                rgrci[nUsed].dwStatus = RIL_CALLSTAT_DIALING;
            }
        }

        // Parse ",<multiparty>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
            goto Continue;
        }
        rgrci[nUsed].dwMultiparty = (nValue ? RIL_CALL_MULTIPARTY : RIL_CALL_SINGLEPARTY);
        rgrci[nUsed].dwParams |= RIL_PARAM_CI_MULTIPARTY;

        // Parse ","
        if (MISPLACED_COMMA_IN_CLCC_CDMA) {
            // Problem: there might be a misplaced comma, which must be skipped.
            if (MatchStringBeginning(szRsp, ",", szDummy) && !MatchStringBeginning(szRsp, ",\"", szDummy) && !MatchStringBeginning(szRsp, ",\0", szDummy))
                (void)MatchStringBeginning(szRsp, ",", szRsp) ;
        }
        if (CLCC_NUMBER_AS_NUMERIC) {
            // Bug in PXS8: while dealing with a multipary CDMA call, the module
            // indicates an empty <address> parameter as 0 (numeric).
            if (MatchStringBeginning(szRsp, ",0,0\r\n", szDummy))
                (void)MatchStringBeginning(szRsp, ",0,0", szRsp) ;
        }
        if (MatchStringBeginning(szRsp, MISPLACED_COMMA_IN_CLCC ? ",," : ",", szRsp)) {
            // Parse "<address>,<type>"
            if (ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)) {
                if(!MatchStringBeginning(szRsp, ",", szRsp))
                    bContinue = TRUE;

                if(!ParseUIntAndVerifyAbove(szRsp, FALSE, 0x1000, nValue, szRsp))
                    bContinue = TRUE;

                nValue = (nValue > 0x100) ? nValue/10 : nValue;
                if ((nValue == 128 || nValue == 129) && g_iLastCOPSOpr == 23405 && strlen(szAddress) < MAXLENGTH_ADDRESS-2 && szAddress[0] != '0') {
                    // Problem with network 23405 (and possibly others):
                    // AT+CLCC sometimes doesn't show the leading '0' in the address, so the '0'
                    // must be added manually.
                    nValue = 129;
                    memmove(szAddress+1, szAddress, MAXLENGTH_ADDRESS-1);
                    szAddress[0] = '0';
                }

                if(!StringToRILAddress(szAddress, (BYTE)nValue, rgrci[nUsed].raAddress))
                    bContinue = TRUE;

                if(bContinue)
                    goto Continue;

                rgrci[nUsed].dwParams |= RIL_PARAM_CI_ADDRESS;
            }
            else {
                // If we couldn't parse an address, then it might be empty,
                // meaning the ID is blocked. Since the address parameter
                // is present, make sure the type also exists before continuing.
                if (!MatchStringBeginning(szRsp, ",", szRsp)                 ||
                    !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp)) {
                    goto Continue;
                }
            }

            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp)) {
                // Parse "<description>"
                if (CLCC_ALPHA_ALWAYS_GSM) {
                    char szGSM[MAXLENGTH_DESCRIPTION+1];

                    if (!ParseString(szRsp, szGSM, MAXLENGTH_DESCRIPTION, szRsp)) {
                        goto Continue;
                    }
                    (void)_snwprintfz(rgrci[nUsed].wszDescription, MAXLENGTH_DESCRIPTION, L"%S", szGSM);
                }
                else {
                    if (!ParseQuotedEncodedString(ENCODING_UCS2, szRsp, rgrci[nUsed].wszDescription, rgrci[nUsed].wszDescription + MAXLENGTH_DESCRIPTION)) {
                        goto Continue;
                    }
                }
                rgrci[nUsed].dwParams |= RIL_PARAM_CI_DESCRIPTION;
            }
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        bSuccess = ParseRspPostfix(szRsp, szRsp);

        if (!bSuccess)
        {
            hr = E_FAIL;
            goto Error;
        }

        // Skip over additional empty line in EHS5.
        if (UNNECESSARY_BLANK_LINES_IN_RESPONSE)
            (void)FindRspPostfix(szRsp, szRsp);
    }

    // Check status of CDMA data connection, it might be necessary
    // to add it to the list of active calls.
    if (CheckCDMADataConnStatus(pDevice, rgrci, nUsed, &oAddInfo)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrci, sizeof(RILCALLINFO), nUsed, &nAllocated, CALLCTRL_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        nNoCalls ++;
        memcpy(&rgrci[nUsed], &oAddInfo, sizeof(RILCALLINFO));
        nUsed++;
    }

    // Check for possible inplausible AT+CLCC response (CDMA).
    if (INPLAUSIBLE_CDMA_CLCC_RESPONSE && fIsCDMA && nUsed == 2) {
        // Bug in PXS8: the AT+CLCC in CDMA has a problem when voice and data
        // call are both active in EVDO (hybrid mode). The module might indicate
        // two data connection instead.
        if (rgrci[0].dwID == 9 && rgrci[1].dwID == 9) {
            // AT+CLCC has incorrectly indicated two data connections. Check if
            // last known state has included 2 calls. It might be better.
            if (iNrCallInfo == 2) {
                // Check if the last known state doesn't also have two data calls.
                if (oCallInfo[0].dwID != 9 || oCallInfo[1].dwID != 9) {
                    // This call state look better, use it.
                    RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Replacing inplausible AT+CLCC response with more plausible one.\r\n")));
                    memcpy(rgrci, oCallInfo, iNrCallInfo * sizeof(RILCALLINFO));
                }
            }
        }
    }

    // Check, if the voice call number has been replaced by the data call number (CDMA)
    if (WRONG_CDMA_CLCC_VOICE_NUMBER && fIsCDMA && nUsed == 2) {
        // Bug in PXS8: the AT+CLCC in CDMA has a problem when voice comes in
        // while there is an active data caonnection. In this case it might happen
        // that the mocule shows the data call number #777 instead of the actual
        // voice call number.
        INT iIdxVoice = -1;

        // Find the voice call in list.
        if (rgrci[0].dwType == RIL_CALLTYPE_VOICE) iIdxVoice = 0;
        if (rgrci[1].dwType == RIL_CALLTYPE_VOICE) iIdxVoice = 1;

        // If there is a voice call in list and the last ^SLCC URC also got 2 calls,
        // check for wrong voice call number.
        if (iIdxVoice >= 0 && iNrCallInfo == 2) {
            // Check if the data call number #777 is in the field of the voice
            // call number.
            if ((rgrci[iIdxVoice].dwParams & RIL_PARAM_CI_ADDRESS) && wcscmp(rgrci[iIdxVoice].raAddress.wszAddress, TEXT(CDMA_DATACONNECTION)) == 0) {
                // The module indicates the data call number. With any luck, the
                // last ^SLCC URC got the right number instead.
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Replacing wrong voice call number in AT+CLCC response.\r\n")));
                memcpy(rgrci, oCallInfo, iNrCallInfo * sizeof(RILCALLINFO));
            }
        }
    }

    // Check for incorrect call direction AT+CLCC response (CDMA).
    if (WRONG_CDMA_CLCC_DATA_CALL_DIRECTION && fIsCDMA && nUsed > 0) {
        INT iIdxData = -1;

        // Find the data call in list.
        if (rgrci[0].dwType == RIL_CALLTYPE_DATA) iIdxData = 0;
        if (nUsed > 1) {
            if (rgrci[1].dwType == RIL_CALLTYPE_DATA) iIdxData = 1;
        }

        // If there is a data call in list, check the call direction.
        if (iIdxData >= 0) {
            if (rgrci[iIdxData].dwDirection == RIL_CALLDIR_INCOMING) {
                // Replace incorrect direction with correct one.
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Replacing wrong data call direction in AT+CLCC response.\r\n")));
                rgrci[iIdxData].dwDirection = RIL_CALLDIR_OUTGOING;
            }
        }
    }

    // Check for wrong ringing state (CDMA)
    if (WRONG_CDMA_CLCC_RINGING_STATE && fIsCDMA && nUsed > 0 && iNrCallInfo > 0) {
        INT iIdxVoice = -1;

        // Find the voice call in list.
        if (rgrci[0].dwType == RIL_CALLTYPE_VOICE) iIdxVoice = 0;
        if (nUsed > 1) {
            if (rgrci[1].dwType == RIL_CALLTYPE_VOICE) iIdxVoice = 1;
        }

        // If there is a voice call in list, check the call direction and status.
        if (iIdxVoice >= 0) {
            if (rgrci[iIdxVoice].dwDirection == RIL_CALLDIR_INCOMING && rgrci[iIdxVoice].dwStatus== RIL_CALLSTAT_INCOMING) {
                INT iIdxVoiceOrg = -1;

                // Find the voice original call in list.
                if (oCallInfo[0].dwType == RIL_CALLTYPE_VOICE) iIdxVoiceOrg = 0;
                if (iNrCallInfo > 1) {
                    if (oCallInfo[1].dwType == RIL_CALLTYPE_VOICE) iIdxVoiceOrg = 1;
                }
                if (iIdxVoiceOrg >= 0) {
                    // Check status in old call list, if it is "active", the AT+CLCC output was wrong.
                    if (oCallInfo[iIdxVoiceOrg].dwDirection == RIL_CALLDIR_INCOMING && oCallInfo[iIdxVoiceOrg].dwStatus== RIL_CALLSTAT_ACTIVE) {
                        // Replace incorrect status with correct one.
                        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Replacing wrong voice call status in AT+CLCC response.\r\n")));
                        rgrci[iIdxVoice].dwStatus = RIL_CALLSTAT_ACTIVE;
                    }
                }
            }
        }
    }


    if (iDoCheck == CALLSTATS_CHK_CHECK)
    {
        BOOL bDoBroadcast = FALSE;

        EnterCriticalSection(&g_CallInfoSection);
        if ((UINT)g_iNrCallInfo != nUsed || AVOID_SLCC) {
            INT i;

            g_iNrCallInfo = (INT)nUsed;
            if (g_iNrCallInfo == 0)
                g_iIdxForFirstCall = -1;
            if (MISSING_SLCC_AT_CALLEND)
                bDoBroadcast = TRUE;
#ifdef RILDRV_CE6
            if (iCallInfoOrg > 0 && g_iNrCallInfo == 0 && AVOID_SLCC)
                SendCE6CallProgressInfo(pDevice, oCallInfo, iNrCallInfo);
#endif
            if (g_iNrCallInfo > 0 && rgrci != NULL) {
                bDoBroadcast = TRUE;

                memcpy(g_CallInfo, (void *)rgrci, (UINT)(g_iNrCallInfo) * sizeof(RILCALLINFO));
                if (g_iIdxForFirstCall > 0) {
                    if (g_CallInfo[0].dwID == 1)
                        g_CallInfo[0].dwID = REPLACEMENT_CALL_ID;
                    if (g_CallInfo[0].dwID == (DWORD)g_iIdxForFirstCall)
                        g_CallInfo[0].dwID = 1;
                }
                if (bIsNewFirstCall && ID_INCREMENTS_AFTER_GPRS && g_iNrCallInfo == 1) {
                    if (g_CallInfo[0].dwID != 1) {
                        RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: changed call ID from %d to 1\r\n"),g_CallInfo[0].dwID));
                        g_iIdxForFirstCall = (INT)g_CallInfo[0].dwID;
                        g_CallInfo[0].dwID = 1;
                    }
                }
                for (i = 0; i < g_iNrCallInfo; i++) {
                    // When making a call via SAT, the SAT application may have given a name in addition
                    // to the number. The name must be used, even when ^SLCC may report a name
                    // from the phonebook instead.
                    if (rgrci[i].dwDirection ==  RIL_CALLDIR_OUTGOING && g_fSATCallActive) {
                        if (pDevice->GetSimToolkit()->SATNameOverrideNeeded(rgrci[i].raAddress.wszAddress, rgrci[i].wszDescription, MAXLENGTH_DESCRIPTION))
                            rgrci[i].dwParams |= RIL_PARAM_CI_DESCRIPTION;
                    }
                }
#ifdef RILDRV_CE6
                SendCE6CallProgressInfo(pDevice, oCallInfo, iNrCallInfo);
#endif
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);

        if (bDoBroadcast && pDevice!=NULL)
            (void)pDevice->BroadcastRealBlobNotification(RIL_NOTIFY_CALLSTATECHANGED, NULL, 0);
    }
    if (iDoCheck == CALLSTATS_CHK_FORCE_CHECK)
    {
        EnterCriticalSection(&g_CallInfoSection);
        g_iNrCallInfo = (INT)nUsed;
        if (g_iNrCallInfo == 0)
            g_iIdxForFirstCall = -1;
        if (g_iNrCallInfo > 0 && rgrci != NULL) {
#ifdef RILDRV_CE6
            iNrCallInfo = g_iNrCallInfo;
            memcpy(oCallInfo, g_CallInfo, MAX_NUMBERCALLS * sizeof(RILCALLINFO));
#endif
            memcpy(g_CallInfo, (void *)rgrci, (UINT)(g_iNrCallInfo) * sizeof(RILCALLINFO));
            if (g_iIdxForFirstCall > 0) {
                if (g_CallInfo[0].dwID == 1)
                    g_CallInfo[0].dwID = REPLACEMENT_CALL_ID;
                if (g_CallInfo[0].dwID == (DWORD)g_iIdxForFirstCall)
                    g_CallInfo[0].dwID = 1;
            }
            if (bIsNewFirstCall && ID_INCREMENTS_AFTER_GPRS && g_iNrCallInfo == 1) {
                if (g_CallInfo[0].dwID != 1) {
                    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: changed call ID from %d to 1\r\n"),g_CallInfo[0].dwID));
                    g_iIdxForFirstCall = (INT)g_CallInfo[0].dwID;
                    g_CallInfo[0].dwID = 1;
                }
            }
#ifdef RILDRV_CE6
            SendCE6CallProgressInfo(pDevice, oCallInfo, iNrCallInfo);
#endif
        }
        LeaveCriticalSection(&g_CallInfoSection);
    }
    if (iDoCheck == CALLSTATS_CHK_ALPHACHECK)
    {
        INT i;

        EnterCriticalSection(&g_CallInfoSection);
#ifdef RILDRV_CE6
        iNrCallInfo = g_iNrCallInfo;
        memcpy(oCallInfo, g_CallInfo, MAX_NUMBERCALLS * sizeof(RILCALLINFO));
#endif
        if ((UINT)g_iNrCallInfo == nUsed && nUsed > 0) {
            for (i=0; i<g_iNrCallInfo; i++) {
                if (rgrci[i].dwDirection ==  RIL_CALLDIR_OUTGOING && g_fSATCallActive) {
                    if (pDevice->GetSimToolkit()->SATNameOverrideNeeded(rgrci[i].raAddress.wszAddress, rgrci[i].wszDescription, MAXLENGTH_DESCRIPTION))
                        rgrci[i].dwParams |= RIL_PARAM_CI_DESCRIPTION;
                }
                if ((g_CallInfo[i].dwParams&RIL_PARAM_CI_DESCRIPTION)==0 || wcslen(g_CallInfo[i].wszDescription)==0) {
                    memcpy(&g_CallInfo[i], &rgrci[i], sizeof(RILCALLINFO));
                }
            }
        }
        LeaveCriticalSection(&g_CallInfoSection);
#ifdef RILDRV_CE6
        if (g_iNrCallInfo >= 0)
            SendCE6CallProgressInfo(pDevice, oCallInfo, iNrCallInfo);
#endif
    }

    if (iDoCheck == CALLSTATS_CHK_NOCHECK)
    {
        SetBacklightIncomingCall((bIncomingCall));
    }

    // If there are no voice calls left, we should check, that the voice path is really deactivated.
    if (nNoVoiceCalls == 0)
    {
        RHADeactivateVoicePath(pDevice, FALSE, FALSE);
    }

    pBlob = (void*)rgrci;
    cbBlob = nUsed * sizeof(RILCALLINFO);

Error:
    if (FAILED(hr) && (rgrci != NULL)) {
        (void)FreeBlob(rgrci);
    }
    return hr;
}

static HRESULT ParseGetCallList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetCallListInternal(CALLSTATS_CHK_NOCHECK, szRsp, pBlob, cbBlob, pParam);
}

HRESULT ParseGetCallListCheck(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetCallListInternal(CALLSTATS_CHK_CHECK, szRsp, pBlob, cbBlob, pParam);
}

HRESULT ParseGetCallListForceCheck(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetCallListInternal(CALLSTATS_CHK_FORCE_CHECK, szRsp, pBlob, cbBlob, pParam);
}

//
//
//
#ifndef RILDRV_CE6
static HRESULT ParseGetFakeCallList(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILCALLINFO* rgrci = NULL;
    HRESULT hr = S_OK;
    BOOL bIncomingCall = FALSE;
    CRilHandle *pDevice;

    pDevice = (CRilHandle *)pParam;

    pBlob = NULL;
    cbBlob = 0;

    if (!AllocateOrReallocateStorage((BYTE**)&rgrci, sizeof(RILCALLINFO), nUsed, &nAllocated, CALLCTRL_ALLOC_NUMBER)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(&rgrci[nUsed], 0x00, sizeof(RILCALLINFO));
    rgrci[nUsed].cbSize = sizeof(RILCALLINFO);
    // The RIL may have been translated the number dialed by the user to "112". The call state should
    // display the number, which the user dialed and not the "112", to which the connection actually
    // is connected to.
    (void)StringToRILAddress(g_szEmergencyNo, (BYTE)129, rgrci[nUsed].raAddress);
    rgrci[nUsed].dwParams |= RIL_PARAM_CI_ADDRESS;
    rgrci[nUsed].raAddress.cbSize = sizeof(RILADDRESS);
    rgrci[nUsed].dwID = 1;
    rgrci[nUsed].dwParams |= RIL_PARAM_CI_ID;
    // Track the call
    rgrci[nUsed].dwDirection = RIL_CALLDIR_OUTGOING;
    rgrci[nUsed].dwParams |= RIL_PARAM_CI_DIRECTION;
    // If the "+CIEV: call,1" URC appeared, the call is no longer in the "dialing" state.
    if (pDevice->GetLastCallURC() == 1)
        rgrci[nUsed].dwStatus = RIL_CALLSTAT_ACTIVE;
    else
        rgrci[nUsed].dwStatus = RIL_CALLSTAT_DIALING;
    rgrci[nUsed].dwParams |= RIL_PARAM_CI_STATUS;
    rgrci[nUsed].dwType = RIL_CALLTYPE_VOICE;
    rgrci[nUsed].dwParams |= RIL_PARAM_CI_TYPE;
    rgrci[nUsed].dwMultiparty = RIL_CALL_SINGLEPARTY;
    rgrci[nUsed].dwParams |= RIL_PARAM_CI_MULTIPARTY;
    nUsed++;

    SetBacklightIncomingCall((bIncomingCall));

    pBlob = (void*)rgrci;
    cbBlob = nUsed * sizeof(RILCALLINFO);

Error:
    if (FAILED(hr) && (rgrci != NULL)) {
        (void)FreeBlob(rgrci);
    }
    return hr;
}

static volatile BOOL g_fFakeEndCLCC = FALSE;

static BOOL IsDialingInAction(const CRilHandle *pRilHandle)
{
    CCommand *pCmd;
    BOOL bFound;

    if (pRilHandle == NULL)
        return FALSE;

    // First check, if there are any
    bFound = FALSE;
    pCmd = pRilHandle->GetCurrentCommand();
    if (pCmd != NULL) {
        if (pCmd->FDial()) {
            return TRUE;
        }
    }
    g_CmdQ.Enum(IsDialCommandInQueue, (DWORD)&bFound, FALSE);
    return bFound;
}

static HRESULT ParseGetCallListSLCC(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    INT nNoCalls, i;
    INT nNoVoiceCalls = 0;
    UINT nAllocated = 0;
    RILCALLINFO* rgrci = NULL;
    HRESULT hr = S_OK;
    BOOL bIncomingCall = FALSE;
    CRilHandle *pDevice;

    pDevice = (CRilHandle *)pParam;
    if (pDevice == NULL) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = NULL;
    cbBlob = 0;
    Sleep(200);         // For Radiotest program only: additional waiting period to simulate execution time of AT+CLCC command.
    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        if (!AllocateOrReallocateStorage((BYTE**)&rgrci, sizeof(RILCALLINFO), 0, &nAllocated, max(CALLCTRL_ALLOC_NUMBER,g_iNrCallInfo))) {
            hr = E_OUTOFMEMORY;
            LeaveCriticalSection(&g_CallInfoSection);
            goto Error;
        }
        if (g_iNrCallInfo > (INT)nAllocated) {
            hr = E_OUTOFMEMORY;
            LeaveCriticalSection(&g_CallInfoSection);
            goto Error;
        }
        memcpy(rgrci, g_CallInfo, (UINT)(g_iNrCallInfo) * sizeof(RILCALLINFO));
        for (i = 0; i < g_iNrCallInfo; i++) {
            if (rgrci[i].dwDirection ==  RIL_CALLDIR_OUTGOING && g_fSATCallActive) {
                if (pDevice->GetSimToolkit()->SATNameOverrideNeeded(rgrci[i].raAddress.wszAddress, rgrci[i].wszDescription, MAXLENGTH_DESCRIPTION))
                    rgrci[i].dwParams |= RIL_PARAM_CI_DESCRIPTION;
            }
        }
    }

    nNoCalls = g_iNrCallInfo;
    LeaveCriticalSection(&g_CallInfoSection);

    for (i=0; i < nNoCalls; i++) {
        switch (rgrci[i].dwStatus)
        {
        case RIL_CALLSTAT_INCOMING:
        case RIL_CALLSTAT_WAITING:
            bIncomingCall = TRUE;
            break;
        default:
            break;
        }
        if (rgrci[i].dwType == RIL_CALLTYPE_VOICE)
            nNoVoiceCalls++;
    }

    SetBacklightIncomingCall((bIncomingCall));

    // If there are no voice calls left, we should check, that the voice path is really deactivated.
    if (nNoVoiceCalls == 0)
    {
        RHADeactivateVoicePath(pDevice, FALSE, FALSE);
    }

    pBlob = (void*)rgrci;
    cbBlob = (UINT)nNoCalls * sizeof(RILCALLINFO);

Error:
    if (FAILED(hr) && (rgrci != NULL)) {
        (void)FreeBlob(rgrci);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetCallList(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    Sleep(50);        // Give the response thread time to parse all URCs

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        DWORD dwWaitPeriod = 250;
        DWORD dwTimeSinceHangup = GetTickCount() - g_dwLastVoiceCmd;

        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("WaitAfterLastVoiceCmd"), &dwWaitPeriod);

        if (dwTimeSinceHangup < dwWaitPeriod)
            Sleep(dwWaitPeriod - dwTimeSinceHangup);

        if (!QueueCmdWithRetry(HCCMD_URCPORT_ONLY, pHandle, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, ParseGetCallList, NULL, hr, CLCC_RETRIES, CLCC_DELAY, NULL, pHandle->GetDevice())) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (SHOULD_WAIT_AFTER_LAST_VOICECMD) {
            DWORD dwWaitPeriod = DEFAULT_WAITAFTERLASTVOICECMD_PERIOD;
            DWORD dwTimeSinceHangup = GetTickCount() - g_dwLastVoiceCmd;

            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("WaitAfterLastVoiceCmd"), &dwWaitPeriod);

            if (dwTimeSinceHangup < dwWaitPeriod)
                Sleep(dwWaitPeriod - dwTimeSinceHangup);
        }

        if (g_fEmergencyCall && (g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) {
            // In the cases "PIN not entered" or "SIM not inserted" the call state commads
            // AT+CLCC or AT^SLCC are not available, so the RIL has to fake the command
            // responses.
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETCALLLIST, ParseGetFakeCallList, NULL, hr, NULL, pHandle->GetDevice())) {
                hr = E_FAIL;
                goto Error;
            }
            g_fFakeEndCLCC = TRUE;
        } else {
            if (g_fFakeEndCLCC || (g_dwSIMLockedState != RIL_LOCKEDSTATE_READY && g_dwSIMLockedState != RIL_LOCKEDSTATE_SIM_PIN2 && g_dwSIMLockedState != RIL_LOCKEDSTATE_SIM_PUK2)) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT\r", CMDOPT_NONE, APIID_GETCALLLIST, ParseGetCallList, NULL, hr, NULL, pHandle->GetDevice())) {
                    hr = E_FAIL;
                    goto Error;
                }
                g_fFakeEndCLCC = FALSE;
            } else {
                if (g_iNrCallInfo > -1 && SUPPORTS_SLCC && !AVOID_SLCC) {
                    INT i;

                    // There is no use to query the call state while the ATD is still in queue, so we will block this call
                    // until the command is sent.
                    while (!g_PowerManager.IsRadioOff()) {
                        if (!IsDialingInAction(pHandle->GetDevice()))
                            break;
                        Sleep(CALLCTRL_FAST_POLLING_INTERVAL);
                    }

                    // We have to prevent, that RILDrv_GetCallList() is executed while a dial command is ongoing
                    // or still in the queue. Otherwise it might happen, that we report to the MMI, that there is no
                    // dialing in progress and the MMI would assume, the dialing command has been aborted,
                    // while actually it hadn't started yet. This would lead to the situation, that the module establishes
                    // the call, while the MMI shows "call ended".
                    i = 0;
                    while (g_LastATD != 0) {
                        Sleep(CALLCTRL_FAST_POLLING_INTERVAL);
                        if (i++ > 50)   // Typically this will take 100ms, 2500ms should be the worst case.
                            break;
                    }
                    g_LastATD = 0;
                    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP|CMDOPT_URC, APIID_GETCALLLIST, ParseGetCallListSLCC, NULL, hr, NULL, (LPVOID)pHandle->GetDevice())) {
                        hr = E_FAIL;
                        goto Error;
                    }
                }
                else {
                    DWORD dwTime;

                    // The module needs a little while to be able to print <alpha> parameter of the
                    // +CLCC response after a +CLIP URC showed up.
                    dwTime = GetTickCount() - g_dwLastCLIPURC;
                    if (dwTime < TIME_AFTER_CLIP)
                        Sleep(TIME_AFTER_CLIP - dwTime);

                    if (!QueueCmdWithRetry(HCCMD_URCPORT_ONLY, pHandle, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, ParseGetCallList, NULL, hr, CLCC_RETRIES, CLCC_DELAY, NULL, pHandle->GetDevice())) {
                        hr = E_FAIL;
                        goto Error;
                    }
                }
            }
        }
    }

Error:
    return hr;
}
#endif

// If the workaround for incrementing caller ID after GPRS is active,
// the RIL corrects the caller IDs, so they start with 1 again. This
// function returns the original caller ID.
static DWORD GetOriginalCallID(DWORD dwID)
{
    DWORD dwOrgID = dwID;

    if (ID_INCREMENTS_AFTER_GPRS && dwID == 1) {
        // The ID number 1 might be not the original one.
        if (g_iIdxForFirstCall > 1)
            dwOrgID = (DWORD)g_iIdxForFirstCall;
    }
    else if (ID_INCREMENTS_AFTER_GPRS && dwID == REPLACEMENT_CALL_ID) {
        // The REPLACEMENT_CALL_ID (ID 7) is the replaced 1.
        if (g_iIdxForFirstCall > 1)
            dwOrgID = 1;
    }

    return dwOrgID;
}

// Find out if given call is currently dialing or alerting to a remote party.
static BOOL IsCallInDialingState(DWORD dwID)
{
    BOOL bDialing = FALSE;

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo >= (INT)dwID && dwID > 0 && dwID <= MAX_NUMBERCALLS) {
        if ((g_CallInfo[dwID-1].dwStatus == RIL_CALLSTAT_DIALING) || (g_CallInfo[dwID-1].dwStatus == RIL_CALLSTAT_ALERTING))
            bDialing = TRUE;
    }
    LeaveCriticalSection(&g_CallInfoSection);

    return bDialing;
}

// Find out if the current call state is "ringing".
static BOOL IsCallInRingingState()
{
    BOOL bRinging = FALSE;

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo == 1) {
        if (g_CallInfo[0].dwStatus == RIL_CALLSTAT_INCOMING)
            bRinging = TRUE;
    }
    LeaveCriticalSection(&g_CallInfoSection);

    return bRinging;
}

// Find out if the current incoming call is a CSD call.
static BOOL IsIncomingCallCSD()
{
    INT iCall;
    BOOL fIncomingCSD = FALSE;

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        for (iCall = 0; iCall < g_iNrCallInfo; iCall++) {
            if (g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_INCOMING || g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_WAITING) {
                if (g_CallInfo[iCall].dwType == RIL_CALLTYPE_DATA)
                    fIncomingCSD = TRUE;
                break;
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    return fIncomingCSD;
}

// Find out if there is any active voice call active
BOOL IsVoiceCallActive(CRilHandle *pDevice)
{
    BOOL bCallActive = FALSE;
    INT iCall;

    if (!SUPPORTS_SLCC || g_iNrCallInfo < 0 || AVOID_SLCC)
        (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pDevice, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListForceCheck, NULL, pDevice);

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        for (iCall=0; iCall < g_iNrCallInfo; iCall++) {
            if (g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_ACTIVE && g_CallInfo[iCall].dwType == RIL_CALLTYPE_VOICE) {
                bCallActive = TRUE;
                break;
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    return bCallActive;
}

#ifdef RILDRV_CE6
static INT GetHoldCall(CRilHandle *pDevice)
{
    INT iCallNr = -1;

    if (!SUPPORTS_SLCC || g_iNrCallInfo < 0 || AVOID_SLCC)
        (void)QueueInternalCmdBlocking(HCCMD_URCPORT_ONLY, pDevice, "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListForceCheck, NULL, pDevice);

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        INT iCall;

        for (iCall=0; iCall < g_iNrCallInfo; iCall++) {
            if (g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_WAITING || g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_ONHOLD) {
                iCallNr = g_CallInfo[iCall].dwID;
                break;
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    return iCallNr;
}
#endif

// Find out if there any incoming call
// Returns alsu the index of the call or 0, if it is the only call.
static BOOL MustPerformSpecialCallReject(PINT piCallNr, PDWORD pdwRejectCause)
{
    INT iCall;
    BOOL bIncoming = FALSE;
    DWORD dwRejectCause = 0;

    *piCallNr = 0;
    *pdwRejectCause = 0;
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RejectCauseIncomingCall"), &dwRejectCause)) {
        if (SEPCIAL_VOICECALL_REJECT_CAUSE)
            dwRejectCause = 21;          // Default Reject Cause: 21 = "user rejected"
    }

    if (dwRejectCause == 0)     // No reject cause available: use default behavior
        return FALSE;
    *pdwRejectCause = dwRejectCause;

    EnterCriticalSection(&g_CallInfoSection);
    if (g_iNrCallInfo > 0) {
        for (iCall = 0; iCall < g_iNrCallInfo; iCall++) {
            if (g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_INCOMING || g_CallInfo[iCall].dwStatus == RIL_CALLSTAT_WAITING) {
                if (g_iNrCallInfo > 1)
                    *piCallNr = (iCall + 1);
                bIncoming = TRUE;
                break;
            }
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);

    return bIncoming;
}

//
//
//
HRESULT RILDrv_ManageCalls(DWORD dwParam, DWORD dwCommand, DWORD dwID)
{
    CNotificationData* pnd = NULL;
    BOOL fNeedNotification = TRUE;
    char szCmd[MAX_ATCMD_LEN];
    DWORD dwOpt = CMDOPT_NONE;
    HRESULT hr = S_OK;
    INT iCallNr = 0;
    BOOL fIsCDMA = FALSE;
    DWORD dwRejectCause = 21;
    HCCMD_MODE iPort = HCCMD_STANDARD;
    PFN_CMD_PARSE pParseOK = NULL;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA)
        fIsCDMA = TRUE;

    switch (dwCommand)
    {
        case RIL_CALLCMD_RELEASEHELD:
            if (IsIncomingCallCSD()) {
                // CSD calls can only be rejected by sending ATH on the data port.
                (void)strncpyz(szCmd, "ATH\r", MAX_ATCMD_LEN);
            }
            else {
                // For a voice call it might be necessary to give a special reject reason
                // using the AT^SHUP command (if available).
                if (SUPPORTS_SHUP && MustPerformSpecialCallReject(&iCallNr, &dwRejectCause)) {
#ifdef RILDRV_CE6
                    g_iLastIdxDisconnected = iCallNr;
#endif
                    if (iCallNr == 0)
                        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SHUP=%d\r", dwRejectCause);
                    else
                        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SHUP=%d,%d\r", dwRejectCause, iCallNr);

                    // In case the reject cause is 17, the problem with CHLD applies, too.
                    if (WRONG_BUSY_TONE_AFTER_CHLD && IsCallInRingingState() && dwRejectCause == 17)
                        pHandle->GetDevice()->SetIgnoreNextCIEVAudioSounderTimer(GetTickCount());
                }
                else {
                    (void)strncpyz(szCmd, "AT+CHLD=0\r", MAX_ATCMD_LEN);
#ifdef RILDRV_CE6
                    g_iLastIdxDisconnected = GetHoldCall(pHandle->GetDevice());
#endif

                    if (WRONG_BUSY_TONE_AFTER_CHLD && IsCallInRingingState())
                        pHandle->GetDevice()->SetIgnoreNextCIEVAudioSounderTimer(GetTickCount());
                }
            }
            dwOpt |= CMDOPT_HANGUP;  // Necessary when attempting to end a single outgoing call
            g_fReleaseHeldCall = TRUE;
            pParseOK = ParseATHOK;
            break;

        case RIL_CALLCMD_RELEASEACTIVE_ACCEPTHELD:
            (void)strncpyz(szCmd, "AT+CHLD=1\r", MAX_ATCMD_LEN);
            break;

#ifdef RILDRV_CE7
        case RIL_CALLCMD_RELEASEWAITINGCALL:
            {
                DWORD dwCallID = 1;
                INT iIdx;

                EnterCriticalSection(&g_CallInfoSection);
                for (iIdx = 0; iIdx < g_iNrCallInfo; iIdx++) {
                    if (g_CallInfo[iIdx].dwStatus == RIL_CALLSTAT_WAITING) {
                        dwCallID = g_CallInfo[iIdx].dwID;
                        break;
                    }
                }
                LeaveCriticalSection(&g_CallInfoSection);
                g_iLastIdxDisconnected = dwCallID;
                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CHLD=1%u\r", dwCallID);
            }
            break;

        case RIL_CALLCMD_RELEASEOFFERING:
            {
                DWORD dwCallID = 1;
                INT iIdx;

                EnterCriticalSection(&g_CallInfoSection);
                for (iIdx = 0; iIdx < g_iNrCallInfo; iIdx++) {
                    if (g_CallInfo[iIdx].dwStatus == RIL_CALLSTAT_INCOMING) {
                        dwCallID = g_CallInfo[iIdx].dwID;
                        break;
                    }
                }
                LeaveCriticalSection(&g_CallInfoSection);

                if (fIsCDMA)
                    (void)strncpyz(szCmd, GetHangupATCommand(pHandle->GetDevice(), &iPort), MAX_ATCMD_LEN);
                else if (SUPPORTS_SHUP)
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SHUP=16,%u\r", dwCallID);
                else
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CHLD=1%u\r", dwCallID);
            }
            break;

        case RIL_CALLCMD_RELEASEALLCALLS:
            g_dwLastVoiceCmd = GetTickCount();
            (void)strncpyz(szCmd, GetHangupATCommand(pHandle->GetDevice(), &iPort), MAX_ATCMD_LEN);
            pParseOK = ParseATHOK;
            break;

        case RIL_CALLCMD_RELEASEHELDCONFCALL:
            (void)strncpyz(szCmd, "AT+CHLD=11;+CHLD=12\r", MAX_ATCMD_LEN);
            break;

        case RIL_CALLCMD_RELEASEPROCEEDING:
            //  No break, intentional fall through.
#endif
        case RIL_CALLCMD_RELEASECALL:
            if (dwID==0)
            {
                // CDMA: use AT+CHV to end call, otherwise AT+HLD can be used.
                if (fIsCDMA)
                    (void)strncpyz(szCmd, GetHangupATCommand(pHandle->GetDevice(), &iPort), MAX_ATCMD_LEN);
                else
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CHLD=1%u\r", GetOriginalCallID(1));
                dwOpt = CMDOPT_HANGUP;
#ifdef RILDRV_CE6
                g_iLastIdxDisconnected = 0;
#endif
                pParseOK = ParseATHOK;
            }
            else if (9 == dwID && fIsCDMA) {
                // In CDMA the ID 9 means the data connection.
                (void)strncpyz(szCmd, "ATH\r", MAX_ATCMD_LEN);
                dwOpt |= CMDOPT_HANGUP;  // Necessary when attempting to end a single outgoing call
            }
            else if (9 == dwID && !fIsCDMA)
            {
                // This is a special-case to enable us to pass FTA case GSM 51.010 31.4.4.2 which requires
                // a way to end all calls in one operation (i.e., "19<SEND>")
                // In Windows CE7 this command is used to hangup data connection (CSD, CDMA).
                g_dwLastVoiceCmd = GetTickCount();
                (void)strncpyz(szCmd, GetHangupATCommand(pHandle->GetDevice(), &iPort), MAX_ATCMD_LEN);
                iPort = HCCMD_URCPORT_ONLY;
                pParseOK = ParseATHOK;
            }
            else if (8 == dwID)
            {
                // This is a special-case to enable us to pass FTA case GSM 51.010 31.4.4.1.2.4 which requires
                // a way to end a held conference call in one operation (i.e., "18<SEND>")
                (void)strncpyz(szCmd, "AT+CHLD=11;+CHLD=12\r", MAX_ATCMD_LEN);
                pParseOK = ParseATHOK;
            }
            else
            {
                // The AT+CHLD=1x command doesn't apply to calls, which are in the
                // dialing or alerting state. The AT^SHUP command (if avalable) can
                // be used as a workaorund in this case.
                // CDMA: there is no AT command to select a special call to end.
                if (fIsCDMA)
                    (void)strncpyz(szCmd, GetHangupATCommand(pHandle->GetDevice(), &iPort), MAX_ATCMD_LEN);
                else if (SUPPORTS_SHUP && IsCallInDialingState(dwID))
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SHUP=16,%u\r", GetOriginalCallID(dwID));
                else
                    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CHLD=1%u\r", GetOriginalCallID(dwID));
#ifdef RILDRV_CE6
                g_iLastIdxDisconnected = GetOriginalCallID(dwID);
#endif
                pParseOK = ParseATHOK;
            }
            break;

        case RIL_CALLCMD_HOLDACTIVE_ACCEPTHELD:
            (void)strncpyz(szCmd, "AT+CHLD=2\r", MAX_ATCMD_LEN);
#ifdef RILDRV_CE6
            pParseOK = ParseCHLDOK;
#endif
            break;

        case RIL_CALLCMD_HOLDALLBUTONE:
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CHLD=2%u\r", GetOriginalCallID(dwID));
            fNeedNotification = FALSE;
            break;

        case RIL_CALLCMD_ADDHELDTOCONF:
            (void)strncpyz(szCmd, "AT+CHLD=3\r", MAX_ATCMD_LEN);
            fNeedNotification = FALSE;
            break;

        case RIL_CALLCMD_ADDHELDTOCONF_DISCONNECT:
            (void)strncpyz(szCmd, "AT+CHLD=4\r", MAX_ATCMD_LEN);
            fNeedNotification = FALSE;
            break;

#ifdef RILDRV_CE7
        case RIL_CALLCMD_ACCEPTWAITINGCALL:
        case RIL_CALLCMD_ACCEPTOFFERINGCALL:
            return RILDrv_Answer(dwParam);
#endif

        default:
            // The proxy code should never let us get here
            hr = E_INVALIDARG;
            goto Error;
    }

    if (fNeedNotification) {
        pnd = new CNotificationData;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_CALLSTATECHANGED, NULL, 0)) {
            delete pnd;
            pnd = NULL;
        }
    }

    g_fCallWaiting = FALSE;
    pHandle->GetDevice()->SetVoiceCallDialing(FALSE);

    if (hr == S_OK) {
        if (!QueueCmd(iPort, pHandle, szCmd, dwOpt, APIID_MANAGECALLS, pParseOK, pnd, hr, NULL, (LPVOID)(pHandle->GetDevice()))) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (pnd)
            delete pnd;
    }
    pnd = NULL;

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_TransferCall(DWORD /*dwParam*/, const RILADDRESS* /*lpAddress*/, const RILSUBADDRESS* /*lpSubAddress*/)
{
    return E_NOTIMPL;
}

#ifndef RILDRV_CE6
static HRESULT ParseGetLineStatusRspCLCC(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    HRESULT hr;
    DWORD* pdwLineStatus = NULL;
    DWORD dwLineStatus;
    INT i;
    CRilHandle *pDevice;

    pDevice = (CRilHandle *)pParam;

    hr = ParseGetCallListInternal(CALLSTATS_CHK_FORCE_CHECK, szRsp, pBlob, cbBlob, pParam);
    if (FAILED(hr))
        goto Error;

    dwLineStatus = RIL_LINESTAT_READY;
    for (i=0; i < g_iNrCallInfo; i++) {
        // In CDMA the data connection might be included into this list. It will
        // show up with ID 9 and must be ignored.
        if (pDevice->GetCurrentTechnology() != TEC_MODE_CDMA || g_CallInfo[i].dwID != 9) {
            if (g_CallInfo[i].dwStatus == RIL_CALLSTAT_ACTIVE || g_CallInfo[i].dwStatus == RIL_CALLSTAT_ONHOLD)
                dwLineStatus = RIL_LINESTAT_CALLINPROGRESS;
            else if (g_CallInfo[i].dwStatus == RIL_CALLSTAT_INCOMING || g_CallInfo[i].dwStatus == RIL_CALLSTAT_WAITING) {
                dwLineStatus = RIL_LINESTAT_RINGING;
                break;
            }
        }
    }

    if (pBlob != NULL)
        FreeBlob(pBlob);

    pBlob = NULL;
    cbBlob = 0;

    pdwLineStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwLineStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwLineStatus, 0x00, sizeof(DWORD));

    *pdwLineStatus = dwLineStatus;

    if(g_fCallWaiting && *pdwLineStatus == RIL_LINESTAT_RINGING)*pdwLineStatus = RIL_LINESTAT_CALLINPROGRESS;

    SetBacklightIncomingCall(RIL_LINESTAT_RINGING == dwLineStatus);

    pBlob = (void*)pdwLineStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pdwLineStatus);
    }
    return hr;
}

static HRESULT ParseGetLineStatusRspSLCC(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    INT i;
    DWORD* pdwLineStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwLineStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwLineStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwLineStatus, 0x00, sizeof(DWORD));

    EnterCriticalSection(&g_CallInfoSection);
    *pdwLineStatus = RIL_LINESTAT_READY;
    for (i=0; i < g_iNrCallInfo; i++) {
        if (g_CallInfo[i].dwStatus == RIL_CALLSTAT_ACTIVE || g_CallInfo[i].dwStatus == RIL_CALLSTAT_ONHOLD)
            *pdwLineStatus = RIL_LINESTAT_CALLINPROGRESS;
        else if (g_CallInfo[i].dwStatus == RIL_CALLSTAT_INCOMING || g_CallInfo[i].dwStatus == RIL_CALLSTAT_WAITING) {
            *pdwLineStatus = RIL_LINESTAT_RINGING;
            break;
        }
    }
    LeaveCriticalSection(&g_CallInfoSection);
    SetBacklightIncomingCall(RIL_LINESTAT_RINGING == *pdwLineStatus);

    if(g_fCallWaiting && (*pdwLineStatus == RIL_LINESTAT_RINGING)) {
        *pdwLineStatus = RIL_LINESTAT_CALLINPROGRESS;
    }
    pBlob = (void*)pdwLineStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pdwLineStatus);
    }
    return hr;
}

HRESULT RILDrv_GetLineStatus(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        DWORD dwWaitPeriod = 250;
        DWORD dwTimeSinceHangup = GetTickCount() - g_dwLastVoiceCmd;

        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("WaitAfterLastVoiceCmd"), &dwWaitPeriod);

        if (dwTimeSinceHangup < dwWaitPeriod)
            Sleep(dwWaitPeriod - dwTimeSinceHangup);

        // Problem: AT+CPAS cannot be used while a PPP connection is active. As a workaround
        // we can extract the value from AT+CLCC.
        if (!QueueCmdWithRetry(HCCMD_URCPORT_ONLY, pHandle, "AT+CLCC\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETLINESTATUS, ParseGetLineStatusRspCLCC, NULL, hr, CLCC_RETRIES, CLCC_DELAY, NULL, (LPVOID)(pHandle->GetDevice()))) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (g_iNrCallInfo > -1 && SUPPORTS_SLCC && !AVOID_SLCC) {
        INT i;

        // There is no use to query the line state while the ATD is still in queue, so we will block this call
        // until the command is sent.
        while (!g_PowerManager.IsRadioOff()) {
            if (!IsDialingInAction(pHandle->GetDevice()))
                break;
            Sleep(CALLCTRL_FAST_POLLING_INTERVAL);
        }

        // We have to prevent, that RILDrv_GetLineStatus() is executed while a dial command is ongoing
        // or still in the queue. Otherwise it might happen, that we report to the MMI, that there is no
        // dialing in progress and the MMI would assume, the dialing command has been aborted,
        // while actually it hadn't started yet. This would lead to the situation, that the module establishes
        // the call, while the MMI shows "call ended".
        // We also have to prevent this function to be executed in case a +CRING URC has shown up, but
        // the corresponding ^SLCC URC hasn't arrived yet. We use g_LastATD for this purpose, too.
        i = 0;
        while (g_LastATD != 0) {
            Sleep(CALLCTRL_FAST_POLLING_INTERVAL);
            if (i++ > 50)   // Typically this will take 100ms, 2500ms should be the worst case.
                break;
        }
        g_LastATD = 0;

        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETLINESTATUS, ParseGetLineStatusRspSLCC, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        DWORD dwTime;

        dwTime = GetTickCount() - g_dwLastCLIPURC;
        if (dwTime < TIME_AFTER_CLIP)
            Sleep(TIME_AFTER_CLIP - dwTime);

        // Problem: AT+CPAS cannot be used while a PPP connection is active. As a workaround
        // we can extract the value from AT+CLCC.
        if (!QueueCmdWithRetry(HCCMD_URCPORT_ONLY, pHandle, "AT+CLCC\r", CMDOPT_GENERATE_ERROR_CODE, APIID_GETLINESTATUS, ParseGetLineStatusRspCLCC, NULL, hr, CLCC_RETRIES, CLCC_DELAY, NULL, (LPVOID)(pHandle->GetDevice()))) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}
#endif

//
//
//
#ifdef RIL_ENABLE_ALS
HRESULT RILDrv_SetCurrentAddressId(DWORD dwParam, DWORD dwAddressId)
{
    HRESULT hr = S_OK;
    char szCmd[MAX_ATCMD_LEN];
    CNotificationData* pnd = NULL;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!pHandle->GetDevice()->IsSIMReady()) {
        hr = RIL_E_SIMNOTINSERTED;
        goto Error;
    }

    if (g_dwSIMLockedState != RIL_LOCKEDSTATE_READY && g_dwSIMLockedState != RIL_LOCKEDSTATE_UNKNOWN) {
        hr = RIL_E_SIMPINREQUIRED;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_CURRENTLINECHANGED, dwAddressId)) {
        delete pnd;
        pnd = NULL;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SALS=1,%u\r", dwAddressId+1);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETCURRENTADDRESSID, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}
#else
HRESULT RILDrv_SetCurrentAddressId(DWORD /*dwParam*/, DWORD /*dwAddressId*/)
{
    return E_NOTIMPL;
}
#endif

//
//
//
#ifdef RIL_ENABLE_ALS
static HRESULT ParseGetCurrentAddressId(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    UINT nDummy;
    DWORD* pdwAddressId = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwAddressId = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwAddressId) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    *pdwAddressId = 0;

    // Parse "<prefix>^SALS: <URC>,<addressID><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "^SALS: ", szRsp)  ||
        !ParseUInt(szRsp, TRUE, nDummy, szRsp)          ||
        !MatchStringBeginning(szRsp, ",", szRsp)        ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwAddressId = nValue-1;

    pBlob = (void*)pdwAddressId;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pdwAddressId);
    }
    return hr;
}
#endif

//
//
//
#ifdef RIL_ENABLE_ALS
HRESULT RILDrv_GetCurrentAddressId(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT^SALS?\r", CMDOPT_NONE, APIID_GETCURRENTADDRESSID, ParseGetCurrentAddressId, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}
#else
HRESULT RILDrv_GetCurrentAddressId(DWORD /*dwParam*/)
{
    return E_NOTIMPL;
}
#endif

HRESULT RILDrv_SendFlash(DWORD dwParam, LPRILADDRESS raRilAddress)
{
    HRESULT hr = S_OK;
    char szCmd[MAX_ATCMD_LEN];
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() != TEC_MODE_CDMA) {
        hr = RIL_E_SERVICEOPTIONNOTSUPPORTED;
        goto Error;
    }

    if (raRilAddress != NULL)
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SFLASH=%S\r", raRilAddress->wszAddress);
    else
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SFLASH\r");
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SENDFLASH, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}
