/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/misc.cpp $
 * $Revision: /main/ms-d/187 $
 *     $Date: 2014-06-19 16:51:03 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/misc.cpp$ 
 * 
 * /main/ms-d/187   2014-06-19 16:51:03   pmackent 
 * Fix exception in RIL_GetCurrentSystemType() (IPIS100097887)
 *
 * /main/ms-d/186   2014-01-09 13:23:15   pmackent
 * Modified ECn0 value
 *
 * /main/ms-d/185   2013-12-02 14:26:00   pmackent
 * Set signal strength thersholds via registry
 *
 * /main/ms-d/184   2013-10-24 13:52:16   pmackent
 * Don't call CDMA only functions with GSM modules
 *
 * /main/ms-d/183   2013-10-01 14:15:19   pmackent
 * Handle case of failing AT^SMONI on EHS5
 *
 * /main/ms-d/182   2013-09-25 15:21:33   pmackent
 * New methods for signal strength display in 3G networks (IPIS100091577)
 *
 * /main/ms-d/181   2013-08-21 13:16:01   pmackent
 * Fix for 2G detection via AT^SCFG (only for old PXS8)
 *
 * /main/ms-d/180   2013-06-27 17:50:23   pmackent
 * Workaround for wrong "null" response in EHS5
 *
 * /main/ms-d/179   2013-05-23 11:53:58   pmackent
 * Handle IMEI / no IMEI case without build option
 *
 * /main/ms-d/178   2013-05-15 15:26:02   pmackent
 * New format for MEID
 *
 * /main/ms-d/177   2013-04-22 16:12:15   pmackent
 * Work on workarounds for AT+COPS/<AcT> and AT^SJDL/ATE
 *
 * /main/ms-d/176   2013-04-18 15:51:18   pmackent
 * Add RIL_GetSignalQuality API for CE7
 *
 * /main/ms-d/175   2013-03-04 13:19:34   pmackent
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/174   2013-02-26 15:07:34   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/173   2013-02-14 15:18:02   pmackent
 * Fix build error on advanced notifications build option
 *
 * /main/ms-d/172   2012-12-13 09:26:08   pmackent
 * Handling of Sprint variant of AT+CSQ
 *
 * /main/ms-d/171   2012-12-12 15:24:04   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/170   2012-11-29 15:31:02   pmackent
 * Adaptions call control for EHS5 under CE6
 *
 * /main/ms-d/169   2012-11-22 10:34:35   pmackent
 * Workaround: no AT+GMM in EHS5, no AT+CGMM in PXS8 (CDMA)
 *
 * /main/ms-d/168   2012-11-21 14:25:24   pmackent
 * Use +GMM/+GMR instead of +CGMM/+CGMR because they won't work in PXS8 airplane mode
 *
 * /main/ms-d/167   2012-11-21 13:30:15   pmackent
 * Basic support of EHS5 Rel2
 *
 * /main/ms-d/166   2012-11-19 15:29:03   pmackent
 * No AT+CSQ in D3 is now optional
 *
 * /main/ms-d/165   2012-11-15 11:46:09   pmackent
 * Avoid sending AT+CSQ in D3 power state (IPIS100083487)
 *
 * /main/ms-d/164   2012-11-01 15:54:42   pmackent
 * Workaround for AT+CSQ problems with PXS8
 *
 * /main/ms-d/163   2012-10-08 16:18:29   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/162   2012-09-18 09:59:22   pmackent
 * Allow commands like AT^SBNW=agps,-1 in AT Tunneling
 *
 * /main/ms-d/161   2012-07-17 15:45:27   pmackent
 * Corrected error code in out of memory case
 *
 * /main/ms-d/160   2012-07-13 13:04:26   pmackent
 * Changes for compatibility with Windows CE6/6
 *
 * /main/ms-d/159   2012-07-03 11:51:50   pmackent
 * Introduce infor about provider list date
 *
 * /main/ms-d/158   2012-06-08 16:59:25   pmackent
 * Add APIs for CDMA voice privacy
 *
 * /main/ms-d/157   2012-05-16 10:41:52   pmackent
 * Fix problem with signal strength in CDMA
 *
 * /main/ms-d/156   2012-04-26 13:02:24   pmackent
 * Further CDMA specific code
 *
 * /main/ms-d/155   2012-04-18 13:46:53   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/154   2012-02-22 13:26:44   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/153   2012-02-16 12:57:03   ctna0256
 * Intel versions of cell tower and position info
 *
 * /main/ms-d/152   2012-02-03 14:01:08   ctna0256
 * Phonebook and SIM access now in separate files
 *
 * /main/ms-d/151   2012-02-03 09:51:32   ctna0256
 * Wildcards for SIM pathes
 *
 * /main/ms-d/150   2012-02-02 17:04:11   ctna0256
 * FD phonebook workaround and NITZ URCs for Intel platform
 *
 * /main/ms-d/149   2012-01-31 16:38:28   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/148   2012-01-05 10:26:41   ctna0256
 * Save PIN2 because of problem with AT+CLCK="FD" in PH8 Rel2 (IPIS100076746)
 *
 * /main/ms-d/147   2011-10-07 16:11:37   ctna0256
 * Small corrections
 *
 * /main/ms-d/239   2011-09-26 11:32:06   ctna0256
 * Comply to Windows CE waring level 4
 *
 * /main/ms-d/145   2011-09-09 09:50:18   ctna0256
 * Remove RIL_DEVSPECIFIC_ACTIVATE_SIMLOCK_WITH_PUK option (IPIS100074643)
 *
 * /main/ms-d/144   2011-09-02 11:03:35   ctna0256
 * Added PUK for customer SIM lock (IPIS100074643)
 *
 * /main/ms-d/143   2011-09-01 16:27:06   ctna0256
 * New devspecific phonelock API (IPIS100074639)
 *
 * /main/ms-d/142   2011-09-01 10:56:30   ctna0256
 * Remove "A" from module name (IPIS100074754)
 *
 * /main/ms-d/141   2011-07-22 11:28:56   ctna0256
 * Filter out "searching" extended notification (IPIS100073242)
 *
 * /main/ms-d/140   2011-06-17 10:40:57   ctna0256
 * New API for extended registration status (IPIS100073242)
 *
 * /main/ms-d/139   2011-03-29 11:37:10   ctna0256
 * Don't use noop queue as default to solve timing issues in conference calls
 *
 * /main/ms-d/138   2011-03-10 15:30:59   ctna0256
 * Special handlig of writing to next free phonebook index in CE7
 *
 * /main/ms-d/137   2011-01-18 10:58:00   ctna0256
 * Added missing MTLR parameters
 *
 * /main/ms-d/136   2010-12-15 10:43:56   ctna0256
 * Performed lint check
 *
 * /main/ms-d/135   2010-11-29 16:21:04   ctna0256
 * Remove internal MONI command
 *
 * /main/ms-d/134   2010-11-26 10:15:51   ctna0256
 * Moved radio configuration from misc.cpp to new file
 *
 * /main/ms-d/133   2010-11-18 10:46:08   ctna0256
 * Remove Prefast warnings
 *
 * /main/ms-d/132   2010-11-16 13:42:27   ctna0256
 * SCFG setting: write setting only, if changed
 *
 * /main/ms-d/131   2010-11-10 15:21:30   ctna0256
 * Adaptions for CE7
 *
 * /main/ms-d/130   2010-11-08 16:00:50   ctna0256
 * Configure antenna diversity
 *
 * /main/ms-d/129   2010-11-05 10:50:07   ctna0256
 * Better handling of "radio off" in SIM commands.
 *
 * /main/ms-d/128   2010-11-04 15:47:11   ctna0256
 * New configuration of SDPORT, C-IMEI and SVN
 *
 * /main/ms-d/127   2010-10-28 16:18:22   ctna0256
 * Workaround for missing SMONI command in PH8
 *
 * /main/ms-d/126   2010-10-26 10:22:27   ctna0256
 * Use SIM path for all CRSM commands
 *
 * /main/ms-d/125   2010-10-12 11:12:05   ctna0256
 * Advanced configuradion for PH8
 *
 * /main/ms-d/124   2010-09-21 13:12:43   ctna0256
 * No phonebook access after enabling PIN on PH8 customer sample
 *
 * /main/ms-d/123   2010-09-08 11:24:24   ctna0256
 * RIL option: automatic GPRS reattach (IPIS100064423)
 *
 * /main/ms-d/122   2010-07-14 10:40:39   ctna0256
 * Preparation: CCUG and ACM may not be supported in PH8
 *
 * /main/ms-d/121   2010-07-09 10:31:14   ctna0256
 * No fast registration mode in PH8
 *
 * /main/ms-d/120   2010-07-02 11:05:16   ctna0256
 * New handling of Customer SIM lock for HC25 R2 (IPIS100065826)
 *
 * /main/ms-d/119   2010-05-28 14:23:42   ctna0256
 * Introducing PH8
 *
 * /main/ms-d/118   2010-05-07 10:18:30   ctna0256
 * New PPP trace feature needed to find connection problems (IPIS100064425)
 *
 * /main/ms-d/117   2010-05-05 13:13:42   ctna0256
 * Support EU3 (for Pre-PH8 testing)
 *
 * /main/ms-d/116   2010-04-14 08:56:42   ctna0256
 * Fix for setting currency unit
 *
 * /main/ms-d/115   2010-02-24 13:25:57   ctna0256
 * Made CE6 driver comply with radiometrics test
 *
 * /main/ms-d/114   2010-02-17 14:26:52   ctna0256
 * GetRegistrySZ() might return TRUE, even on error (IPIS100062294)
 *
 * /main/ms-d/113   2010-02-04 13:21:18   ctna0256
 * Enable manual network selection per default
 *
 * /main/ms-d/112   2010-02-04 11:44:51   ctna0256
 * Use registry to enable/disable the manual network selection in the MMI according to SIM file EF_CSP
 *
 * /main/ms-d/111   2010-02-03 10:50:57   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/110   2010-01-26 09:55:46   ctna0256
 * New API: RIL_GetPositionInfo()
 *
 * /main/ms-d/109   2010-01-22 13:20:30   ctna0256
 * UDI and SyncPPP not supported in future module types
 *
 * /main/ms-d/108   2009-10-28 08:26:14   ctna0256
 * Correct PIN counter query on 2G SIMs to the requirements of GSM11.11
 *
 * /main/ms-d/107   2009-09-21 12:15:02   ctna0256
 * Increase max version number for HC28-J
 *
 * /main/ms-d/106   2009-09-21 08:54:32   ctna0256
 * Add support of HC28-J
 *
 * /main/ms-d/105   2009-09-04 09:21:38   ctna0256
 * Workaround for wrong 2G/3G-SIM detection in module (IPIS100057390)
 *
 * /main/ms-d/104   2009-07-13 09:34:47   ctna0256
 * Removed unnecessary extern declaration
 *
 * /main/ms-d/103   2009-05-28 11:36:38   ctna0256
 * Remove "Siemens" from RIL
 *
 * /main/ms-d/102   2009-04-08 10:34:06   ctna0256
 * Changed error codes for unavailble SIM record status
 *
 * /main/ms-d/101   2009-04-06 14:20:25   ctna0256
 * Read IMSI before using AT+CRSM
 *
 * /main/ms-d/100   2009-02-23 13:16:05   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/99   2009-02-04 13:37:11   ctna0256
 * Query PIN2 retry counter on USIMs
 *
 * /main/ms-d/98   2009-01-22 10:06:12   ctna0256
 * Change default value for length of SIM file status request
 *
 * /main/ms-d/97   2009-01-14 11:52:23   ctna0450
 * Consider possible absence of mux driver in driver version check
 *
 * /main/ms-d/96   2009-01-14 10:50:02   ctna0450
 * Removed special handling for consecutive WaitCommEvent() calls because mux ports are properly instancable now
 *
 * /main/ms-d/95   2008-12-02 16:14:38   ctna0256
 * Special configuration for UDI
 *
 * /main/ms-d/94   2008-12-02 11:12:58   ctna0256
 * Special default value for fast registration mode
 *
 * /main/ms-d/93   2008-11-25 10:21:32   ctna0256
 * SIM path not for HC25 R1
 *
 * /main/ms-d/92   2008-11-25 09:46:55   ctna0256
 * Workaround for false error on selecting DF or MF
 * Configure SIM path via registry
 *
 * /main/ms-d/91   2008-10-22 15:28:05   ctna0256
 * Added missing break
 *
 * /main/ms-d/90   2008-10-21 15:05:12   ctna0256
 * Registry key to force use of AT+CSQ
 *
 * /main/ms-d/89   2008-10-16 15:12:01   ctna0256
 * Support "ForbiddenSIMFile" key for ECC SIM field
 *
 * /main/ms-d/88   2008-10-15 15:02:09   ctna0256
 * Fix for write to log option
 *
 * /main/ms-d/87   2008-10-15 11:40:27   ctna0256
 * New registry keys for determing the system type
 *
 * /main/ms-d/86   2008-10-06 09:24:57   ctna0256
 * Remove support of extended phonebook
 *
 * /main/ms-d/85   2008-09-29 08:43:56   ctna0256
 * Extended phonebook not available in official HC28R2
 *
 * /main/ms-d/84   2008-09-23 10:09:22   ctna0256
 * Registry key for extended PB support
 *
 * /main/ms-d/83   2008-09-23 08:50:13   ctna0256
 * Improved handling of writing og extended phonebook entries
 *
 * /main/ms-d/82   2008-09-18 15:24:43   ctna0256
 * Introduction of extended phonebook support in WM6  AKU 1.3
 *
 * /main/ms-d/81   2008-08-29 10:21:30   ctna0256
 * Now supporting HC28 Relese 2
 *
 * /main/ms-d/80   2008-08-26 14:26:37   ctna0256
 * Merged RIL_RADIO_RESILIENCE option from Classic-RIL
 *
 * /main/ms-d/79   2008-08-22 10:24:06   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/78   2008-08-21 09:34:35   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/77   2008-07-24 09:41:04   mp49210
 * New option: RIL_DEVSPECIFIC_WRITE_TO_LOG
 *
 * /main/ms-d/76   2008-07-22 15:52:53   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/75   2008-07-22 10:45:14   mp49210
 * Changed handling of emergency calls for Korea
 *
 * /main/ms-d/74   2008-07-16 12:04:26   mp49210
 * Workaround for bug in Microsoft MMI with handling of ECC fields
 *
 * /main/ms-d/73   2008-07-09 09:45:40   mp49210
 * Registry key for retries for ECC SIM access
 *
 * /main/ms-d/72   2008-06-20 11:05:46   mp49210
 * Remove unused SIEMENS_MAGNETO define
 *
 * /main/ms-d/71   2008-06-13 09:36:46   mp49210
 * IPIS100048706: force re-read of phone book after SIM file change
 *
 * /main/ms-d/70   2008-06-12 15:00:20   mp49210
 * Block writing to phonebook during calls, if there had been an incoming call without number
 *
 * /main/ms-d/69   2008-06-03 14:59:12   mp49210
 * Fix for "unknown SIM status" error.
 *
 * /main/ms-d/68   2008-05-30 13:31:49   ht37010
 * added debug zone ZONE_PORT
 *
 * /main/ms-d/67   2008-05-15 15:02:04   mp49210
 * Use ECC configuration from radio
 *
 * /main/ms-d/66   2008-04-29 13:39:57   mp49210
 * Removed Lint warning
 *
 * /main/ms-d/65   2008-04-21 08:16:43   mp49210
 * Handling of AT&Ts network selection menue change
 *
 * /main/ms-d/64   2008-04-16 16:24:14   mp49210
 * Debug zones for RHA traces
 *
 * /main/ms-d/63   2008-04-03 16:13:08   mp49210
 * Don't repeat AT+CRSM=192 if SIM is not inserted
 *
 * /main/ms-d/62   2008-04-03 14:41:39   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/61   2008-04-02 14:11:37   mp49210
 * Lint check
 *
 * /main/ms-d/60   2008-03-14 09:57:09   mp49210
 * Correct use of retries for SIM get response command
 *
 * /main/ms-d/59   2008-03-13 11:55:14   mp49210
 * Handling of ECC SIM files of 3G SIMs
 * Remove no longer needed polling option
 *
 * /main/ms-d/58   2008-02-29 16:25:43   mp49210
 * Remove potential use of NULL pointer
 *
 * /main/ms-d/57   2008-02-27 15:57:19   mp49210
 * Workaround for problems with PIN/PUK counter in the enable/disable PIN dialog for 3G SIMs
 *
 * /main/ms-d/56   2008-02-08 13:14:10   mp49210
 * Prevent start of module, if IMEI is missing
 *
 * /main/ms-d/55   2008-01-29 11:51:49   mp49210
 * New AT-Tunneling
 *
 * /main/ms-d/54   2007-12-19 15:15:50   mp49210
 * Trace of multiplexer version
 * Allow HC25 up to version 2.099
 *
 * /main/ms-d/53   2007-12-11 16:22:12   mp49210
 * Read debug options from registry at attach of rilgsm.dll
 *
 * /main/ms-d/52   2007-11-19 09:38:14   mp49210
 * Now supported: HC25A
 *
 * /main/ms-d/51   2007-11-16 09:50:45   mp49210
 * Trigger check of PIN status, when SIM command had been blocked because of unkown SIM status
 *
 * /main/ms-d/50   2007-11-14 15:16:20   mp49210
 * Introduction of MTLR
 *
 * /main/ms-d/49   2007-11-09 09:50:44   mp49210
 * Reset HC28 version number to correct value
 *
 * /main/ms-d/48   2007-11-06 14:54:08   ht37010
 * new retail trace if ioctrl RIL_DEVSPECIFIC_FORCEEQSTATEOFF is handled
 *
 * /main/ms-d/47   2007-11-05 16:31:54   ht37010
 * set Modem version for HC28 to Revision 02.000
 *
 * /main/ms-d/46   2007-09-20 12:10:42   mp49210
 * No useful SIM phonebooks on HC25/HC28 customer samples
 *
 * /main/ms-d/45   2007-09-11 10:15:42   mp49210
 * Support HC25 Rel2 customer samples
 *
 * /main/ms-d/44   2007-09-10 15:21:33   mp49210
 * Changed syntax of AT+WS46 command in HC28
 *
 * /main/ms-d/43   2007-08-31 15:06:44   mp49210
 * Allow the restriction of SIM access for special SIM files.
 *
 * /main/ms-d/42   2007-08-10 16:56:15   mp49210
 * IPIS100040302: send AT+CSIM after AT+CPIN?
 *
 * /main/ms-d/41   2007-08-08 11:16:44   mp49210
 * Introduction of audio profile download
 *
 * /main/ms-d/40   2007-08-02 11:21:34   mp49210
 * Introduction of HC28
 *
 * /main/ms-d/39   2007-07-13 16:46:01   mp49210
 * New feature RIL_GetCellTowerInfo()
 *
 * /main/ms-d/38   2007-07-09 10:15:36   mp49210
 * Size limit in kByte instead of Byte
 *
 * /main/ms-d/37   2007-07-04 08:57:15   mp49210
 * Introduction of maximum trace file length
 *
 * /main/ms-d/36   2007-07-04 07:32:06   mp49210
 * Introduction of radio band group search
 *
 * /main/ms-d/35   2007-06-28 09:58:46   mp49210
 * Close handle to threads to prevent handle leaks in memory
 *
 * /main/ms-d/34   2007-05-29 09:34:58   mp49210
 * Changed behaviour of cost info commands.
 *
 * /main/ms-d/33   2007-05-02 10:26:21   mp49210
 * New registry key "NoRoaming Indicator"
 *
 * /main/ms-d/32   2007-04-27 08:33:52   mp49210
 * Removed Lint warnings
 *
 * /main/ms-d/31   2007-04-26 16:03:21   mp49210
 * Change for PreFast compatibility
 *
 * /main/ms-d/30   2007-04-24 15:58:12   mp49210
 * IPIS100035501: introduction of list of networks (registry), where the RIL should not send the roaming status
 *
 * /main/ms-d/29   2007-04-11 08:25:09   mp49210
 * Extended version info
 *
 * /main/ms-d/28   2007-04-05 17:17:47   mp49210
 * Workaround for UICC <-> GSM differences in SIM files
 *
 * /main/ms-d/27   2007-03-29 11:48:35   mp49210
 * Workaround for SIM-PIN counter in GSM SIMs
 *
 * /main/ms-d/26   2007-03-29 10:22:49   mp49210
 * Prevent exception while parsing error messages when module is off
 *
 * /main/ms-d/25   2007-03-28 10:42:02   mp49210
 * Store CLIR value persistent
 *
 * /main/ms-d/24   2007-02-26 16:03:46   mp49210
 * No default value for system type
 *
 * /main/ms-d/23   2007-02-22 16:44:32   mp49210
 * Workaround for unusable AT+CRSM=242 on USIMs
 *
 * /main/ms-d/22   2007-02-21 13:50:01   mp49210
 * HC25 no longer supports AT^SBC
 *
 * /main/ms-d/21   2007-02-09 15:15:11   mp49210
 * Lint compatibility
 *
 * /main/ms-d/20   2007-01-16 15:43:33   wm36264
 * Added trace of version info for HcUsbDrv.dll and HcCmdApi.dll
 *
 * /main/ms-d/19   2007-01-05 16:17:38   mp49210
 * Show OS Build No in version info
 *
 * /main/ms-d/18   2007-01-04 14:26:12   mp49210
 * Renamed Registry-Keys implieng the existence of a multiplexer.
 *
 * /main/ms-d/17   2007-01-02 15:03:33   mp49210
 * Remove some features not supported by Quinn
 *
 * /main/ms-d/16   2006-12-27 14:20:02   mp49210
 * Workaround for wrong CME-error at AT+CPBR
 *
 * /main/ms-d/15   2006-12-22 15:06:58   mp49210
 * HC25 doesn't support cell tower info
 *
 * /main/ms-d/14   2006-12-21 17:19:00   mp49210
 * Modified AT+CRSM syntax
 *
 * /main/ms-d/13   2006-12-19 18:13:09   mp49210
 * Some changes for commands used during initialization
 *
 * /main/ms-d/12   2006-12-19 11:34:55   mp49210
 * Addotions for AT^SIND=psinfo
 *
 * /main/ms-d/11   2006-12-18 17:46:04   mp49210
 * Introduction of AT^SIND=psinfo
 *
 * /main/ms-d/10   2006-12-15 17:12:46   mp49210
 * Update calll management capabilities.
 * Prevent 2G/3G switch while active call.
 *
 * /main/ms-d/9   2006-12-14 16:41:15   mp49210
 * Modified behaviour of AT+CRSM
 *
 * /main/ms-d/8   2006-11-21 09:02:14   mp49210
 * New GPRS auto attach mechanism
 *
 * /main/ms-d/7   2006-11-17 14:38:13   mp49210
 * Temporarily disable AT+CRSM functionality
 *
 * /main/ms-d/6   2006-11-17 14:24:39   mp49210
 * HC25 supports EN phonebook
 *
 * /main/ms-d/5   2006-11-17 11:32:53   wm36264
 * Removed HC15 from modem list because HC25 is available now
 *
 * /main/ms-d/4   2006-11-13 10:05:12   mp49210
 * New PIN2 handling in Quinn
 *
 * /main/ms-d/3   2006-11-09 14:24:39   mp49210
 * Quinn specific feature: selction of 2G / 3G network
 *
 * /main/ms-d/2   2006-11-01 10:29:42   mp49210
 * New AT+CFUN handling in Quinn
 *
 * /main/ms-d/1   2006-10-25 16:53:44   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 "..\..\inc\rilversion.h"

extern DWORD g_dwRadioAudioProfilesVersion;

BOOL  g_bCINDIsActive = FALSE;
UINT  g_iLastRSSI = 99;
UINT  g_iLastSignal = 99;
char g_szSequenceFromDialer[MAX_ATCMD_LEN] = "";

//
// Bit error rates
//
static const DWORD g_rgdwBitErrorRates[] =
{
    14,     // 0
    28,     // 1
    57,     // 2
    113,    // 3
    226,    // 4
    453,    // 5
    905,    // 6
    1810,   // 7
};
#define NUM_BITERRORRATES     (sizeof(g_rgdwBitErrorRates) / sizeof(DWORD))

#if defined (IOCTL_RIL_GETANTENNASTATUS)
// Antenna Status
static const DWORD g_rgdwAntennaStatus[] =
{
    RIL_ANTENNASTATUS_CONNECTED,       // 0
    RIL_ANTENNASTATUS_SHORTEDGROUND,   // 1
    RIL_ANTENNASTATUS_SHORTED,         // 2
    RIL_ANTENNASTATUS_DISCONNECTED,    // 3
};
#define NUM_ANTENNASTATUS      (sizeof(g_rgdwAntennaStatus) / sizeof(DWORD))
#endif

DWORD g_n2G3GSelection = SELECTION2G3G_UNKNOWN;

static const LISTVALUEMAP g_RoamingMode[] =
{
    {0,  RIL_ROAMMODE_HOMEONLY               },
    {1,  RIL_ROAMMODE_AUTOMATICA  }
};
#define NUM_ROAMINGMODES (sizeof(g_RoamingMode) / sizeof(LISTVALUEMAP))

// Relevant only if RIL_USE_CSQPOLLING is not set: force CSQ polling.
DWORD s_dwUseCSQ = 0;

// dBm value equvalent to 1 to 5 bars on the antenna status icon.
DWORD s_dwRssi1Bar  = RSSI_1_BAR;
DWORD s_dwRssi2Bars = RSSI_2_BARS;
DWORD s_dwRssi3Bars = RSSI_3_BARS;
DWORD s_dwRssi4Bars = RSSI_4_BARS;
DWORD s_dwRssi5Bars = RSSI_5_BARS;

// Steps for RSCP values.
INT s_iRSCPlimit5 = -80;
INT s_iRSCPlimit4 = -90;
INT s_iRSCPlimit3 = -100;
INT s_iRSCPlimit2 = -106;
INT s_iRSCPlimit1 = -125;

// Steps for EC/n0 values.
INT s_iECN0limit5 = -9;
INT s_iECN0limit4 = -11;
INT s_iECN0limit3 = -13;
INT s_iECN0limit2 = -15;
INT s_iECN0limit1 = -19;

// Steps for RSRP values.
INT s_iRSRPlimit5 = -70;
INT s_iRSRPlimit4 = -80;
INT s_iRSRPlimit3 = -90;
INT s_iRSRPlimit2 = -100;
INT s_iRSRPlimit1 = -140;

// Steps for RSRQ values.
INT s_iRSRQlimit5 = -7;
INT s_iRSRQlimit4 = -10;
INT s_iRSRQlimit3 = -13;
INT s_iRSRQlimit2 = -16;
INT s_iRSRQlimit1 = -20;

//
// Read registry keys relevant for signal strength.
//
void ReadMiscRegKeys()
{
    DWORD dwTemp;

    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ForceUseOfCSQ"), &s_dwUseCSQ))
        s_dwUseCSQ = 0;
#if defined(RIL_USE_CSQPOLLING)
    if (s_dwUseCSQ != 0) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Registry key \"ForceUseOfCSQ\" has been ignored.\r\n")));
        s_dwUseCSQ = 0;
    }
#endif

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SignalStrength1Bar"), &s_dwRssi1Bar);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SignalStrength2Bars"), &s_dwRssi2Bars);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SignalStrength3Bars"), &s_dwRssi3Bars);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SignalStrength4Bars"), &s_dwRssi4Bars);
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("SignalStrength5Bars"), &s_dwRssi5Bars);

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSCPLimit1"), &dwTemp))
        s_iRSCPlimit1 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSCPLimit2"), &dwTemp))
        s_iRSCPlimit2 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSCPLimit3"), &dwTemp))
        s_iRSCPlimit3 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSCPLimit4"), &dwTemp))
        s_iRSCPlimit4 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSCPLimit5"), &dwTemp))
        s_iRSCPlimit5 = -1 * (INT)dwTemp;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ECN0Limit1"), &dwTemp))
        s_iECN0limit1 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ECN0Limit2"), &dwTemp))
        s_iECN0limit2 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ECN0Limit3"), &dwTemp))
        s_iECN0limit3 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ECN0Limit4"), &dwTemp))
        s_iECN0limit4 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ECN0Limit5"), &dwTemp))
        s_iECN0limit5 = -1 * (INT)dwTemp;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRPLimit1"), &dwTemp))
        s_iRSRPlimit1 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRPLimit2"), &dwTemp))
        s_iRSRPlimit2 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRPLimit3"), &dwTemp))
        s_iRSRPlimit3 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRPLimit4"), &dwTemp))
        s_iRSRPlimit4 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRPLimit5"), &dwTemp))
        s_iRSRPlimit5 = -1 * (INT)dwTemp;

    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRQLimit1"), &dwTemp))
        s_iRSRQlimit1 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRQLimit2"), &dwTemp))
        s_iRSRQlimit2 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRQLimit3"), &dwTemp))
        s_iRSRQlimit3 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRQLimit4"), &dwTemp))
        s_iRSRQlimit4 = -1 * (INT)dwTemp;
    if (GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RSRQLimit5"), &dwTemp))
        s_iRSRQlimit5 = -1 * (INT)dwTemp;
}

//
//
//
static HRESULT ParseGetEquipmentInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILEQUIPMENTINFO* prei = NULL;
    HRESULT hr = S_OK;
    DWORD dwReg = 1;

    pBlob = NULL;
    cbBlob = 0;

    prei = (RILEQUIPMENTINFO*)AllocBlob(sizeof(RILEQUIPMENTINFO));
    if (!prei) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prei, 0x00, sizeof(RILEQUIPMENTINFO));
    prei->cbSize = sizeof(RILEQUIPMENTINFO);

    // Parse "<prefix><manufacturer_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                       ||
        !ParseUnquotedString(szRsp, '\r', prei->szManufacturer, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_MANUFACTURER;

    // Parse "<prefix><model_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                ||
        !ParseUnquotedString(szRsp, '\r', prei->szModel, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_MODEL;

    // The module might modify the model name by adding an "A". This can be
    // removed by the RIL.
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("RemoveAfromModule"), &dwReg);
    if (dwReg == 1) {
        char *pTmp;
        pTmp = strstr(prei->szModel, " A");
        if (pTmp != NULL)
            *pTmp = '\0';
    }

    // Parse "<prefix><revision_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                   ||
        !ParseUnquotedString(szRsp, '\r', prei->szRevision, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_REVISION;

    // Parse "<prefix><serial_number><postfix>"
    // The serial number (IMSI/MEID) might not be available.
    if (!ParseRspPrefix(szRsp, szRsp)                                                       ||
        !ParseUnquotedString(szRsp, '\r', prei->szSerialNumber, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
    }

    if (strcmp(prei->szSerialNumber, "OK") == 0 || strlen(prei->szSerialNumber) == 0) {
        // The serial number might not be available. In this case there will
        // be the "OK" in the parser. Remove it and clear the field.
        memset(prei->szSerialNumber, 0, sizeof(prei->szSerialNumber));
    }
    else {
        prei->dwParams |= RIL_PARAM_EI_SERIALNUMBER;

        // In CDMA the GSN will be the MEID which has a leading "0x".
        if (prei->szSerialNumber[0] == '0' && prei->szSerialNumber[1] == 'x') {
            char szMEID[MAXLENGTH_EQUIPINFO];

            dwReg = 1;
            memset(szMEID, 0, MAXLENGTH_EQUIPINFO);
            if (strlen(prei->szSerialNumber) > 18) prei->szSerialNumber[18] = '\0';
            while (strlen(prei->szSerialNumber) < 18) strncatz(prei->szSerialNumber, "0", MAXLENGTH_EQUIPINFO-strlen(prei->szSerialNumber));

            // There are two possible formats: 14-digit hex and 18-digit decimal
            // (see 3GPP2 X.S0008-0). The RIL allows both formats.
            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("MEIDinDecimalRepresentation"), &dwReg);
            if (dwReg == 1) {
                ULONG ulMC, ulSN;

                // Decimal representation: print number as 18-digit dec.
                ulSN = strtoul(prei->szSerialNumber + 12, NULL, 16);
                prei->szSerialNumber[12] = '\0';
                ulMC = strtoul(prei->szSerialNumber, NULL, 16);
                (void)_snprintfz(szMEID, MAXLENGTH_EQUIPINFO, "%010u%08u", ulMC, ulSN);
            }
            else {
                // Hexadecimal representation: print number as 14-dgit hex.
                (void)_snprintfz(szMEID, MAXLENGTH_EQUIPINFO, "%s", prei->szSerialNumber+4);
                szMEID[14] = '\0';

            }
            (void)_snprintfz(prei->szSerialNumber, MAXLENGTH_EQUIPINFO, "%s", szMEID);
        }
    }

    pBlob = (void*)prei;
    cbBlob = sizeof(RILEQUIPMENTINFO);

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


//
//
//
HRESULT RILDrv_GetEquipmentInfo(DWORD dwParam)
{
    HRESULT hr = S_OK;
    const char *szCmdA;
    const char *szCmdB;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Problem with some versions of the PXS8: AT+CGMx might not work and
    // must be substituted with AT+GMx.
    if (pHandle->GetDevice()->UseGMMratherThanCGMM()) {
        szCmdA = "AT+GMI;+GMM;+GMR;+GSN\r";
        // The (customer) IMEI might not be written yet, so try without AT+GSN as well.
        szCmdB = "AT+GMI;+GMM;+GMR\r";
    }
    else {
        szCmdA = "AT+CGMI;+CGMM;+CGMR;+CGSN\r";
        // The (customer) IMEI might not be written yet, so try without AT+CGSN as well.
        szCmdB = "AT+CGMI;+CGMM;+CGMR\r";
    }

    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmdA, szCmdB, CMDOPT_PLAN_B, APIID_GETEQUIPMENTINFO, ParseGetEquipmentInfo, NULL, hr, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetEquipmentState(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILEQUIPMENTSTATE * pres = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pres = (RILEQUIPMENTSTATE*)AllocBlob(sizeof(RILEQUIPMENTSTATE));
    if (!pres) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    g_PowerManager.FillEquipmentStateStruct(pres, g_PowerManager.GetEquipmentState());

    pBlob = (void*)pres;
    cbBlob = pres->cbSize;

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


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

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

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_IGNORERADIOOFF|CMDOPT_NOOP, APIID_GETEQUIPMENTSTATE, ParseGetEquipmentState, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetEquipmentState(DWORD dwParam, DWORD dwEquipmentState)
{
  HRESULT hr;

  CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

  if (!pHandle) {
    return E_INVALIDARG;
  }

  switch (dwEquipmentState) {
    case RIL_EQSTATE_MINIMUM:
#ifdef RIL_RADIO_RESILIENCE
      g_fAllowReboot = FALSE;
      ClearPINSecure();
#endif
      ClearPIN2Secure();
    case RIL_EQSTATE_DISABLETXANDRX:
    case RIL_EQSTATE_FULL:
      if (!g_PowerManager.EnterEquipmentState(pHandle, dwEquipmentState)) {
        return E_FAIL;
      }
      hr = S_OK;
      (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
      return hr;
    case RIL_EQSTATE_DISABLETX:
    case RIL_EQSTATE_DISABLERX:
      return E_NOTIMPL;
    default:
      ;
  }
  return E_INVALIDARG;
}


//
//
//
static HRESULT ParseGetCostInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILCOSTINFO* prci = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prci = (RILCOSTINFO*)AllocBlob(sizeof(RILCOSTINFO));
    if (!prci) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prci, 0x00, sizeof(RILCOSTINFO));
    prci->cbSize = sizeof(RILCOSTINFO);

    // Parse "<prefix>+CAOC: \"<ccm>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CAOC: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwCCM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_CCM;

    // Parse "<prefix>+CACM: \"<acm>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CACM: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwACM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_ACM;

    // Parse "<prefix>+CAMM: \"<acm_max>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CAMM: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwMaxACM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_MAXACM;

    // Parse "<prefix>+CPUC: <currency>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPUC: ", szRsp) ||
        !ParseQuotedEncodedString(ENCODING_UCS2, szRsp, prci->wszCurrency, prci->wszCurrency + MAXLENGTH_CURRENCY)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwParams |= RIL_PARAM_CSTI_CURRENCY;

    // Parse "\"<ppu>\"<postfix>"
    if (!MatchStringBeginning(szRsp, ",\"", szRsp)      ||
        !ParseFixedPoint(szRsp, prci->dwCostPerUnit, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)       ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwParams |= RIL_PARAM_CSTI_COSTPERUNIT;

    pBlob = (void*)prci;
    cbBlob = sizeof(RILCOSTINFO);

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


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

    if (!QueueCmd(HCCMD_URCPORT_ONLY, pHandle, "AT+CAOC=0;+CACM?;+CAMM?;+CPUC?\r", CMDOPT_NONE, APIID_GETCOSTINFO, ParseGetCostInfo, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_SetCostInfo(DWORD dwParam, const RILCOSTINFO* lpCostInfo, LPCSTR lpszPassword)
{
    char szCmd[MISC_CMDBUF_LENGTH_COST];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (!SUPPORTS_ADVICE_OF_CHARGE) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (!(lpCostInfo->dwParams & RIL_PARAM_CSTI_ACM) && !(lpCostInfo->dwParams & RIL_PARAM_CSTI_MAXACM) &&
        (!(lpCostInfo->dwParams & RIL_PARAM_CSTI_COSTPERUNIT) ||
         !(lpCostInfo->dwParams & RIL_PARAM_CSTI_CURRENCY))) {
        hr = E_INVALIDARG;
        goto Error;
    }

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

    if (lpCostInfo->dwParams & RIL_PARAM_CSTI_ACM) {
        // Add "+CAOC="
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CACM=");
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        // Add "<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }

    if (lpCostInfo->dwParams & RIL_PARAM_CSTI_MAXACM) {
        // Add ";", if this is not the first command
        if (szWalk != szCmd + 2) {  // NO_TYPO: 30
            (void)strncpyz(szWalk, ";", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
            szWalk++;
        }

        // Add "+CAMM=<acm_max>"
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CAMM=%u", lpCostInfo->dwMaxACM);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        // Add ",<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }

    if ((lpCostInfo->dwParams & RIL_PARAM_CSTI_COSTPERUNIT) &&
        (lpCostInfo->dwParams & RIL_PARAM_CSTI_CURRENCY)) {
        // Add ";", if this is not the first command
        if (szWalk != szCmd + 2) {  // NO_TYPO: 30
            (void)strncpyz(szWalk, ";", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
            szWalk++;
        }

        // Add "+CPUC=<currency>,<ppu>"
        (void)strncpyz(szWalk, "+CPUC=\"", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        char szEncodedText[MAX_ATCMD_LEN];

        (void)UCS2ToGSMHex((WCHAR *)lpCostInfo->wszCurrency, wcslen(lpCostInfo->wszCurrency), szEncodedText, sizeof(szEncodedText));
        (void)strncpyz(szWalk, szEncodedText, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "\",\"%u.%.4u\"",
                         (lpCostInfo->dwCostPerUnit>>16),((lpCostInfo->dwCostPerUnit & 0xFFFF) * 10000)>>16);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        // Add ",<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }
    (void)strncpyz(szWalk, "\r", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmdCMET(HCCMD_URCPORT_ONLY, pHandle, szCmd, CMDOPT_NONE, APIID_SETCOSTINFO, NULL, NULL, hr, NULL)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

UINT g_nRSSI = 28;
UINT g_nBER = 0;

void FillRSQStruct(RILSIGNALQUALITY* prsq)
{
    memset(prsq, 0x00, sizeof(RILSIGNALQUALITY));
    prsq->cbSize = sizeof(RILSIGNALQUALITY);

    // Calculate signal strength
    prsq->nMinSignalStrength = -112;
    prsq->nMaxSignalStrength = -51;

    prsq->nLowSignalStrength = -110;
    prsq->nHighSignalStrength = -60;

    if (99 == g_iLastRSSI) {
        prsq->nSignalStrength = (int)RIL_SIGNALSTRENGTH_UNKNOWN;
    } else {
        prsq->nSignalStrength = (15 * (int)g_iLastRSSI) + prsq->nMinSignalStrength;
        if (prsq->nSignalStrength > prsq->nMaxSignalStrength)   // This happens if +CIEV: rssi,<rssi> reports values <rssi> > 4 which means maximum signal strength
            prsq->nSignalStrength = prsq->nMaxSignalStrength;
    }
    prsq->dwParams |= (RIL_PARAM_SQ_SIGNALSTRENGTH | RIL_PARAM_SQ_MINSIGNALSTRENGTH | RIL_PARAM_SQ_MAXSIGNALSTRENGTH | RIL_PARAM_SQ_LOWSIGNALSTRENGTH | RIL_PARAM_SQ_HIGHSIGNALSTRENGTH);

    // Set bit error rate
    if (NUM_BITERRORRATES > g_iLastSignal) {
        prsq->dwBitErrorRate = g_rgdwBitErrorRates[g_iLastSignal];
    } else {
        prsq->dwBitErrorRate = RIL_BITERRORRATE_UNKNOWN;
    }
    prsq->dwParams |= RIL_PARAM_SQ_BITERRORRATE;

    return;
}

//
// GetRSSIfromCINDvalue()
// The AT^SIND=rssi command return the signal strength in steps from 0 to 5. In
// difference the AT+CSQ command uses steps from 0 to 30. This function translates
// the steps of AT^SIND to the the ones from AT+CSQ in a way that Windows Mobile
// will display 0 to 5 bars in the antenna icon.
//
// Parameter:
// nRSSISIND: value accoring to AT^SIND=rssi
//
// Return value: steps as used in AT+CSQ
//
UINT GetRSSIfromCINDvalue(UINT nRSSISIND)
{
    UINT nRSSI = 0;

    if (nRSSISIND == 1)
        nRSSI = s_dwRssi1Bar;
    else if (nRSSISIND == 2)
        nRSSI = s_dwRssi2Bars;
    else if (nRSSISIND == 3)
        nRSSI = s_dwRssi3Bars;
    else if (nRSSISIND == 4)
        nRSSI = s_dwRssi4Bars;
    else if (nRSSISIND == 5)
        nRSSI = s_dwRssi5Bars;

    return nRSSI;
}

//
// ParseRSSIFromSMONI()
// Depending on the setting of registry key "SignalStrength3G" the RIL might not
// use the RSSI value from AT+CSQ when registered to a 3G network. Instead the
// AT^SMONI command is used to find out about the signal quality using the RSCP
// and Ec/N0 value.
// The RIL will map the RSCP / Ec/N0 values to RSSI values which correspond to
// the five antenna quality bars in the status bar of Windows Mobile.
//
// Parameters:
// rszPointer [in,out]: response string containing the AT^SMONI response
// nRSSI [out]: RSSI value or equivalent for 3G
//
// Return value: TRUE
//
BOOL ParseRSSIFromSMONI(LPCSTR& rszPointer, UINT& nRSSI)
{
    UINT nDummy;

    (void)ParseRspPrefix(rszPointer, rszPointer);

    if (MatchStringBeginning(rszPointer, "^SMONI: 3G,", rszPointer)) {
        INT iECN0 = 0;
        INT iRSCP = 0;
        UINT uiECN0 = 0;
        UINT uiRSCP = 0;

        // This a 3G network? If configured, generate RSSI from RSCP and Ecn0.
        if (g_dwSignalStrenth3G != SS_3G_RSSI) {
            // Parse parameters, there might not be any, if the module is currently in searching mode.
            if (!ParseUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // UARFCN (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // PSC (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseIntRounded(rszPointer, iECN0, rszPointer) ||   // Ec/N0
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseIntRounded(rszPointer, iRSCP, rszPointer) ||   // RSCP
                 !MatchStringAnywhere(rszPointer, "\r\n", rszPointer)) return TRUE;

            if (iRSCP >= s_iRSCPlimit5) uiRSCP = 5;
            else if (iRSCP >= s_iRSCPlimit4) uiRSCP = 4;
            else if (iRSCP >= s_iRSCPlimit3) uiRSCP = 3;
            else if (iRSCP >= s_iRSCPlimit2) uiRSCP = 2;
            else if (iRSCP >= s_iRSCPlimit1) uiRSCP = 1;

            if (iECN0 >= s_iECN0limit5) uiECN0 = 5;
            else if (iECN0 >= s_iECN0limit4) uiECN0 = 4;
            else if (iECN0 >= s_iECN0limit3) uiECN0 = 3;
            else if (iECN0 >= s_iECN0limit2) uiECN0 = 2;
            else if (iECN0 >= s_iECN0limit1) uiECN0 = 1;

            if (g_dwSignalStrenth3G == SS_3G_ECN0) {
                if (uiRSCP < uiECN0) {nRSSI = GetRSSIfromCINDvalue(uiRSCP);g_iLastRSSI = uiRSCP;}
                else {nRSSI = GetRSSIfromCINDvalue(uiECN0);g_iLastRSSI = uiECN0;}
            }
            else {
                nRSSI = GetRSSIfromCINDvalue(uiRSCP);
                g_iLastRSSI = uiRSCP;
            }
        }
    }
    else if (MatchStringBeginning(rszPointer, "^SMONI: 4G,", rszPointer)) {
        INT iDummy;
        INT iRSRP = 0;
        INT iRSRQ = 0;
        UINT uiRSRP = 0;
        UINT uiRSRQ = 0;

        // This a LTE network? If configured, generate RSSI from RSCP and Ecn0.
        if (g_dwSignalStrenth4G != SS_LTE_RSSI) {
            // Parse parameters, there might not be any, if the module is currently in searching mode.
            if (!ParseUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // EARFCN (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // Band (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // DL Band (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // UL Band (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // Mode (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // MCC (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // MNC (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // TAC (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // Global ID (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseHexUInt(rszPointer, TRUE, nDummy, rszPointer) ||   // Phys ID (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseIntRounded(rszPointer, iDummy, rszPointer) ||   // SrxLev (not of interesst)
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseIntRounded(rszPointer, iRSRP, rszPointer) ||   // RSRP
                 !MatchStringBeginning(rszPointer, ",", rszPointer) ||
                 !ParseIntRounded(rszPointer, iRSRQ, rszPointer) ||   // RSRQ
                 !MatchStringAnywhere(rszPointer, "\r\n", rszPointer)) return TRUE;

            if (iRSRP >= s_iRSRPlimit5) uiRSRP = 5;
            else if (iRSRP >= s_iRSRPlimit4) uiRSRP = 4;
            else if (iRSRP >= s_iRSRPlimit3) uiRSRP = 3;
            else if (iRSRP >= s_iRSRPlimit2) uiRSRP = 2;
            else if (iRSRP >= s_iRSRPlimit1) uiRSRP = 1;

            if (iRSRQ >= s_iRSRQlimit5) uiRSRQ = 5;
            else if (iRSRQ >= s_iRSRQlimit4) uiRSRQ = 4;
            else if (iRSRQ >= s_iRSRQlimit3) uiRSRQ = 3;
            else if (iRSRQ >= s_iRSRQlimit2) uiRSRQ = 2;
            else if (iRSRQ >= s_iRSRQlimit1) uiRSRQ = 1;

            if (g_dwSignalStrenth4G == SS_LTE_RSRQ) {
                if (uiRSRP < uiRSRQ) {nRSSI = GetRSSIfromCINDvalue(uiRSRP);g_iLastRSSI = uiRSRP;}
                else {nRSSI = GetRSSIfromCINDvalue(uiRSRQ);g_iLastRSSI = uiRSRQ;}
            }
            else {
                nRSSI = GetRSSIfromCINDvalue(uiRSRP);
                g_iLastRSSI = uiRSRP;
            }
        }
    }

    return TRUE;
}

//
//
//
static HRESULT ParseGetSignalQuality(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nRSSI;
    UINT nBER;
    RILSIGNALQUALITY* prsq = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prsq = (RILSIGNALQUALITY*)AllocBlob(sizeof(RILSIGNALQUALITY));
    if (!prsq) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsq, 0x00, sizeof(RILSIGNALQUALITY));
    prsq->cbSize = sizeof(RILSIGNALQUALITY);

    if (szRsp == NULL)
    {
        // No response string means: the RIL hasn't sent the AT+CSQ command, but
        // the latest signal strength has already been stored.
        FillRSQStruct(prsq);
    }
    else
    {
        if (!ParseRspPrefix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (MatchStringBeginning(szRsp, "+CSQ: ", szRsp)) {
            // Parse "<prefix>+CSQ: <rssi>,<ber><postfix>"
            if (!ParseUIntAndVerifyAbove(szRsp, TRUE, 100, nRSSI, szRsp) ||
                !MatchStringBeginning(szRsp, ",", szRsp)                 ||
                !ParseUInt(szRsp, TRUE, nBER, szRsp)                     ||
                !ParseRspPostfix(szRsp, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
        }
        else if (MatchStringBeginning(szRsp, "^SIND: rssi,", szRsp)) {
            UINT nDummy;
            UINT nRSSISIND;

            // Parse "<prefix>^SIND: rssi,<n>,<rssi><postfix>"
            if (!ParseUInt(szRsp, FALSE, nDummy, szRsp) ||
                !MatchStringBeginning(szRsp, ",", szRsp)                 ||
                !ParseUInt(szRsp, TRUE, nRSSISIND, szRsp)                     ||
                !ParseRspPostfix(szRsp, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            nBER = 99;
            nRSSI = GetRSSIfromCINDvalue(nRSSISIND);
        }
        else {
            hr = E_FAIL;
            goto Error;
        }

        if (!ParseRSSIFromSMONI(szRsp, nRSSI)) {
            hr = E_FAIL;
            goto Error;
        }

        g_nRSSI = nRSSI;
        g_nBER = nBER;

        // Calculate signal strength
        prsq->nMinSignalStrength = -113;
        prsq->nMaxSignalStrength = -51;

        prsq->nLowSignalStrength = -110;
        prsq->nHighSignalStrength = -60;

        if (99 == nRSSI) {
            prsq->nSignalStrength = (int)RIL_SIGNALSTRENGTH_UNKNOWN;
        } else {
            prsq->nSignalStrength = (2 * (int)nRSSI) + prsq->nMinSignalStrength;
        }
        prsq->dwParams |= (RIL_PARAM_SQ_SIGNALSTRENGTH | RIL_PARAM_SQ_MINSIGNALSTRENGTH | RIL_PARAM_SQ_MAXSIGNALSTRENGTH | RIL_PARAM_SQ_LOWSIGNALSTRENGTH | RIL_PARAM_SQ_HIGHSIGNALSTRENGTH);

        // Set bit error rate
        if (NUM_BITERRORRATES > nBER) {
            prsq->dwBitErrorRate = g_rgdwBitErrorRates[nBER];
        } else {
            prsq->dwBitErrorRate = RIL_BITERRORRATE_UNKNOWN;
        }
        prsq->dwParams |= RIL_PARAM_SQ_BITERRORRATE;
    }
    pBlob = (void*)prsq;
    cbBlob = sizeof(RILSIGNALQUALITY);

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

HRESULT ParseGetSignalQualityError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam, DWORD& dwCode) {
    HRESULT hr = E_FAIL;
    void* pBlobTmp = NULL;
    UINT cbBlobTmp = 0;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    hr = ParseGetSignalQuality(szRsp, pBlobTmp, cbBlobTmp, pParam);
    if (SUCCEEDED(hr)) {
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = pBlobTmp;
        cbBlob = cbBlobTmp;
        dwCode = RIL_RESULT_OK;
    }
    return hr;
}

#ifndef RILDRV_CE6
//
//
//
HRESULT RILDrv_GetSignalQuality(DWORD dwParam)
{
    HRESULT hr = S_OK;
    BOOL bSendNOOPCommand = FALSE;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
#ifndef RIL_USE_CSQPOLLING
    // No CSQ Pollling: use NOOP command instead of actually sending AT+CSQ.
    if (g_bCINDIsActive && s_dwUseCSQ == 0 && (pHandle->GetDevice()->GetCurrentTechnology() != TEC_MODE_CDMA || !AVOID_SIND_RSSI_IN_CDMA))
        bSendNOOPCommand = TRUE;
#endif
    // If the radio is in the state where all URCs are off, the RIL should
    // avoid sending an AT+CSQ command, even it CSQ-Polling is on. This
    // option is activated with the "NoCSQinLimitedUrcMode" registry key.
    if (g_fNoCSQinSuspend && g_PowerManager.GetCurrentModemState() >= MODEMSTATE_URCCONFIG_CALL)
        bSendNOOPCommand = TRUE;
    if (bSendNOOPCommand) {
        if (!QueueCmd(HCCMD_NOOP_PORT, pHandle, NULL, CMDOPT_NOOP, APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr, NULL, (LPVOID)(pHandle->GetDevice()))) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        const char *szCmd1 = "AT+CSQ;^SMONI\r";
        const char *szCmd2 = "AT^SIND=rssi,2;^SMONI\r";

        if (SUBSTITUTE_CSQ_WITH_SIND && pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA) {
            szCmd1 = "AT^SIND=rssi,2\r";
            szCmd2 = "AT+CSQ\r";
        }
        if (USE_CSQ_READ && pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA && !SUBSTITUTE_CSQ_WITH_SIND) {
            szCmd1 = "AT+CSQ?\r";
            szCmd2 = "AT^SIND=rssi,2\r";
        }
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd1, szCmd2, CMDOPT_GENERATE_ERROR_CODE|(SUBSTITUTE_CSQ_WITH_SIND || USE_CSQ_READ ? CMDOPT_PLAN_B : CMDOPT_NONE), APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr, 0, 1, 100, ParseGetSignalQualityError, (LPVOID)(pHandle->GetDevice()))) {
            hr = E_FAIL;
            goto Error;
        }
    }
Error:
    return hr;
}
#endif

DWORD WINAPI ReCheckSignalStrengthThreadProc(LPVOID pVoid)
{
    RILSIGNALQUALITY rsq;
    CRilHandle* pHandle = (CRilHandle *)pVoid;

    // Check the current call status and notifiy the MMI if actual call status differs from the chached status.
    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CSQ;^SMONI\r", CMDOPT_NONE, APIID_GETSIGNALQUALITY, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetSignalQuality, ParseGetSignalQualityError, pVoid)) {
        FillRSQStruct(&rsq);
        (void)pHandle->BroadcastRealBlobNotification(RIL_NOTIFY_SIGNALQUALITY, &rsq, sizeof(RILSIGNALQUALITY));
    }
    else if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+CSQ\r", CMDOPT_NONE, APIID_GETSIGNALQUALITY, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetSignalQuality, ParseGetSignalQualityError, pVoid)) {
        FillRSQStruct(&rsq);
        (void)pHandle->BroadcastRealBlobNotification(RIL_NOTIFY_SIGNALQUALITY, &rsq, sizeof(RILSIGNALQUALITY));
    }
    return 0;
}

#ifdef RILDRV_CE7
HRESULT RILDrv_GetSignalQuality(DWORD dwParam, RILSIGNALQUALITY* pSignalQuality)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (pSignalQuality != NULL && s_dwUseCSQ == 1) {
        const char *szCmd1 = "AT+CSQ;^SMONI\r";
        const char *szCmd2 = "AT^SIND=rssi,2;^SMONI\r";

        if (SUBSTITUTE_CSQ_WITH_SIND && pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA) {
            szCmd1 = "AT^SIND=rssi,2\r";
            szCmd2 = "AT+CSQ\r";
        }
        if (USE_CSQ_READ && pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA && !SUBSTITUTE_CSQ_WITH_SIND) {
            szCmd1 = "AT+CSQ?\r";
            szCmd2 = "AT^SIND=rssi,2\r";
        }
        if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szCmd1, CMDOPT_NONE, APIID_GETSIGNALQUALITY, 0, 0, 0, ParseGetSignalQuality, NULL, (LPVOID)(pHandle->GetDevice()))) {
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szCmd2, CMDOPT_NONE, APIID_GETSIGNALQUALITY, 0, 0, 0, ParseGetSignalQuality, NULL, (LPVOID)(pHandle->GetDevice()));
        }
    }

    if (pSignalQuality != NULL) {
        FillRSQStruct(pSignalQuality);
        TraceIOCTLGetSignalStrengthCE7(pSignalQuality);
    }
    else {
        DWORD dwOption = CMDOPT_GENERATE_ERROR_CODE;
        const char *szCmd1 = "AT+CSQ;^SMONI\r";
        const char *szCmd2 = "AT^SIND=rssi,2;^SMONI\r";

        if (SUBSTITUTE_CSQ_WITH_SIND && pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA) {
            szCmd1 = "AT^SIND=rssi,2\r";
            szCmd2 = "AT+CSQ\r";
        }
        if (USE_CSQ_READ && pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA && !SUBSTITUTE_CSQ_WITH_SIND) {
            szCmd1 = "AT+CSQ?\r";
            szCmd2 = "AT^SIND=rssi,2\r";
        }
        if (SUBSTITUTE_CSQ_WITH_SIND) dwOption |= CMDOPT_PLAN_B;
        if (USE_CSQ_READ) dwOption |= CMDOPT_PLAN_B;
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd1, szCmd2, dwOption, APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr, 0, 1, 100, ParseGetSignalQualityError, (LPVOID)(pHandle->GetDevice()))) {
            hr = E_FAIL;
            goto Error;
        }
    }
Error:
    return hr;
}
#endif

//
//
//
static HRESULT ParseGetCellTowerInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    RILCELLTOWERINFO* prcti = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcti = (RILCELLTOWERINFO*)AllocBlob(sizeof(RILCELLTOWERINFO));
    if (!prcti) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcti, 0x00, sizeof(RILCELLTOWERINFO));
    prcti->cbSize = sizeof(RILCELLTOWERINFO);

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

    // Different parameter sets for 2G and 3G networks.
    if (MatchStringBeginning(szRsp, "2G", szRsp)) {     // 2G network
        // Parse "<afrcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<bcch>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseIntAsUInt(szRsp, nValue, szRsp, FALSE);
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwMobileCountryCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_MOBILECOUNTRYCODE;
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwMobileNetworkCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_MOBILENETWORKCODE;
        }
        // Parse "<lac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwLocationAreaCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_LOCATIONAREACODE;
        }
        // Parse "<cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwCellID = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_CELLID;
        }
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szRsp, "3G", szRsp)) {        // 3G network
        DWORD dwValue = 0;

        // Parse "<uarfcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<psc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<ec>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)MatchStringBeginning(szRsp, "-", szRsp);
        (void)ParseFixedPoint(szRsp, dwValue, szRsp);
        // Parse "<rscp>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseIntAsUInt(szRsp, nValue, szRsp, FALSE);
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwMobileCountryCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_MOBILECOUNTRYCODE;
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwMobileNetworkCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_MOBILENETWORKCODE;
        }
        // Parse "<lac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwLocationAreaCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_LOCATIONAREACODE;
        }
        // Parse "<cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwCellID = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_CELLID;
        }
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szRsp, "4G", szRsp)) {        // LTE network
        DWORD dwValue = 0;

        // Parse "<earfcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<band>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<DL bw>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<UL bw>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<mode>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseIntAsUInt(szRsp, nValue, szRsp, FALSE);
		if(!MatchStringBeginning(szRsp, "FDD", szRsp) && !MatchStringBeginning(szRsp, "TDD", szRsp)){
		    hr = E_FAIL;
            goto Error;
		}
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwMobileCountryCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_MOBILECOUNTRYCODE;
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwMobileNetworkCode = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_MOBILENETWORKCODE;
        }
        // Parse "<tac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseHexUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<global cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prcti->dwCellID = nValue;
            prcti->dwParams |= RIL_PARAM_CTI_CELLID;
        }
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prcti;
    cbBlob = sizeof(RILCELLTOWERINFO);
Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prcti);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetCellTowerInfoIntel(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    UINT nLAC;
    UINT nCellID;
    WCHAR wszNetwork[UCS2_LEN];
    RILCELLTOWERINFO* prcti = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcti = (RILCELLTOWERINFO*)AllocBlob(sizeof(RILCELLTOWERINFO));
    if (!prcti) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcti, 0x00, sizeof(RILCELLTOWERINFO));
    prcti->cbSize = sizeof(RILCELLTOWERINFO);

    // Parse "<prefix>+COPS: <mode>,<format>,<opName>,<AcT>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+COPS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUCS2String(szRsp, wszNetwork, UCS2_LEN, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<prefix>+CREG: <mode>,<regStatus>,<LAC>,<CellID>,[<AcT>]"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nLAC, szRsp) ||
        !MatchStringBeginning(szRsp, "\",\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nCellID, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp) ||
        !FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (wcslen(wszNetwork) >= 5 && wcslen(wszNetwork) <= 6) {
        prcti->dwMobileNetworkCode = _wtoi(wszNetwork+3);
        wszNetwork[3] = 0;
        prcti->dwMobileCountryCode = _wtoi(wszNetwork);
        prcti->dwParams |= RIL_PARAM_CTI_MOBILECOUNTRYCODE|RIL_PARAM_CTI_MOBILENETWORKCODE;
    }
    prcti->dwLocationAreaCode = nLAC;
    prcti->dwCellID = nCellID;
    prcti->dwParams |= RIL_PARAM_CTI_LOCATIONAREACODE|RIL_PARAM_CTI_CELLID;

    pBlob = (void*)prcti;
    cbBlob = sizeof(RILCELLTOWERINFO);
Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prcti);
    }
    return hr;
}

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

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

    if (SMONI_UNAVAILABLE) {
        // The EHS5 doesn't suppoprt network monitoring command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+COPS=3,2;+COPS?;+CREG?\r", CMDOPT_NONE, APIID_GETCELLTOWERINFO, ParseGetCellTowerInfoIntel, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // The most of the cell tower variable are available with AT^SMONI
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT^SMONI\r", CMDOPT_NONE, APIID_GETCELLTOWERINFO, ParseGetCellTowerInfo, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}

#if defined(IOCTL_RIL_GETPOSITIONINFO)
//
//
//
static HRESULT ParseGetPositionInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nRSSI;
    UINT nBER;
    UINT nValue;
    RILPOSITIONINFO* prpi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prpi = (RILPOSITIONINFO*)AllocBlob(sizeof(RILPOSITIONINFO));
    if (!prpi) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prpi, 0x00, sizeof(RILPOSITIONINFO));
    prpi->cbSize = sizeof(RILPOSITIONINFO);

    GetSystemTime(&prpi->timestamp);

    if (!ParseRspPrefix(szRsp, szRsp)                            ||
        !MatchStringBeginning(szRsp, "+CSQ: ", szRsp)            ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 100, nRSSI, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp)                 ||
        !ParseUInt(szRsp, TRUE, nBER, szRsp)                     ||
        !ParseRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
    }

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

    // Different parameter sets for 2G and 3G networks.
    if (MatchStringBeginning(szRsp, "2G", szRsp)) {     // 2G network

        prpi->dwType = RIL_POSITIONTYPE_GSM;

#ifndef RILDRV_CE7
        if (nRSSI <= 1)
            prpi->positionInfoGSM.dwRxLevel = 0;
        else if (nRSSI == 31)
            prpi->positionInfoGSM.dwRxLevel = 63;
        else if (nRSSI < 31)
            prpi->positionInfoGSM.dwRxLevel = 3 + (2*(nRSSI-2));
        else
            prpi->positionInfoGSM.dwRxLevel = 0;
#endif

        // Parse "<afrcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
#ifndef RILDRV_CE7
            prpi->positionInfoGSM.dwARFCN = nValue;
#endif
        }
        // Parse "<bcch>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseIntAsUInt(szRsp, nValue, szRsp, FALSE);
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoGSM.dwMobileCountryCode = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_MOBILECOUNTRYCODE;
#endif
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoGSM.dwMobileNetworkCode = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_MOBILENETWORKCODE;
#endif
        }
        // Parse "<lac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoGSM.dwLocationAreaCode = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_LOCATIONAREACODE;
#endif
        }
        // Parse "<cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoGSM.dwCellID = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_CELLID;
#endif
        }
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<ncc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szRsp, "3G", szRsp)) {        // 3G network

        prpi->dwType = RIL_POSITIONTYPE_UMTS;

        // Parse "<uarfcn>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
#ifndef RILDRV_CE7
            prpi->positionInfoUMTS.dwUARFCN = nValue;
#endif
        }
        // Parse "<psc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoUMTS.dwPrimaryScramblingCode = nValue;
        }
        // Parse "<ec>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        // Parse "<rscp>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)MatchStringBeginning(szRsp, "-", szRsp);  // skip '-' sign (if present)
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            // Translate from dBm according 3GPP 05.08.
#ifndef RILDRV_CE7
            if (nValue > 100)
                prpi->positionInfoUMTS.dwRSCP = 0;
            else if (nValue < 48)
                prpi->positionInfoUMTS.dwRSCP = 63;
            else
                prpi->positionInfoUMTS.dwRSCP = 63 - (nValue-48);
#endif
        }
        // Parse "<mcc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoUMTS.dwMobileCountryCode = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_MOBILECOUNTRYCODE;
#endif
        }
        // Parse "<mnc>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoUMTS.dwMobileNetworkCode = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_MOBILENETWORKCODE;
#endif
        }
        // Parse "<lac>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
#ifndef RILDRV_CE7
            prpi->positionInfoUMTS.dwLocationAreaCode = nValue;
            prpi->dwParams |= RIL_PARAM_POSITION_LOCATIONAREACODE;
#endif
        }
        // Parse "<cell>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
            prpi->positionInfoUMTS  .dwCellID = nValue;
#ifndef RILDRV_CE7
            prpi->dwParams |= RIL_PARAM_POSITION_CELLID;
#endif
        }
        // Parse "<c1>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        (void)ParseUInt(szRsp, TRUE, nValue, szRsp);
        if (!FindRspPostfix(szRsp, szRsp)) {       // Skip over the rest
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prpi;
    cbBlob = sizeof(RILPOSITIONINFO);
Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prpi);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetPositionInfoIntel(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nRSSI;
    UINT nBER;
    UINT nValue;
    UINT nAcT;
    UINT nLAC;
    UINT nCellID;
    WCHAR wszNetwork[UCS2_LEN];
    RILPOSITIONINFO* prpi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prpi = (RILPOSITIONINFO*)AllocBlob(sizeof(RILPOSITIONINFO));
    if (!prpi) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prpi, 0x00, sizeof(RILPOSITIONINFO));
    prpi->cbSize = sizeof(RILPOSITIONINFO);

    GetSystemTime(&prpi->timestamp);

    // Parse "<prefix>+CSQ: <rssi>,<ber>"
    if (!ParseRspPrefix(szRsp, szRsp)                            ||
        !MatchStringBeginning(szRsp, "+CSQ: ", szRsp)            ||
        !ParseUIntAndVerifyAbove(szRsp, TRUE, 100, nRSSI, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp)                 ||
        !ParseUInt(szRsp, TRUE, nBER, szRsp)                     ||
        !ParseRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
    }

    // Parse "<prefix>+COPS: <mode>,<format>,<opName>,<AcT>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+COPS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUCS2String(szRsp, wszNetwork, UCS2_LEN, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nAcT, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<prefix>+CREG: <mode>,<regStatus>,<LAC>,<CellID>,[<AcT>]"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !MatchStringBeginning(szRsp, ",\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nLAC, szRsp) ||
        !MatchStringBeginning(szRsp, "\",\"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nCellID, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp) ||
        !FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    prpi->dwType = nAcT == 0 ? RIL_POSITIONTYPE_GSM : RIL_POSITIONTYPE_UMTS;

#ifndef RILDRV_CE7
    if (nRSSI <= 1)
        prpi->positionInfoGSM.dwRxLevel = 0;
    else if (nRSSI == 31)
        prpi->positionInfoGSM.dwRxLevel = 63;
    else if (nRSSI < 31)
        prpi->positionInfoGSM.dwRxLevel = 3 + (2*(nRSSI-2));
    else
        prpi->positionInfoGSM.dwRxLevel = 0;
#endif

    if (wcslen(wszNetwork) >= 5 && wcslen(wszNetwork) <= 6) {
        prpi->positionInfoGSM.dwMobileNetworkCode = _wtoi(wszNetwork+3);
        wszNetwork[3] = 0;
        prpi->positionInfoGSM.dwMobileCountryCode = _wtoi(wszNetwork);
#ifndef RILDRV_CE7
        prpi->dwParams |= RIL_PARAM_POSITION_MOBILECOUNTRYCODE|RIL_PARAM_POSITION_MOBILENETWORKCODE;
#endif
    }
    prpi->positionInfoGSM.dwLocationAreaCode = nLAC;
#ifndef RILDRV_CE7
    prpi->dwParams |= RIL_PARAM_POSITION_LOCATIONAREACODE;
#endif
    prpi->positionInfoGSM.dwCellID = nCellID;
#ifndef RILDRV_CE7
    prpi->dwParams |= RIL_PARAM_POSITION_CELLID;
#endif

    pBlob = (void*)prpi;
    cbBlob = sizeof(RILPOSITIONINFO);
Error:
    if (FAILED(hr)) {
        (void)FreeBlob(prpi);
    }
    return hr;
}

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

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    if (SMONI_UNAVAILABLE) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+COPS=3,2;+CSQ;+COPS?;+CREG?\r", CMDOPT_NONE, APIID_GETPOSITIONINFO, ParseGetPositionInfoIntel, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CSQ;^SMONI\r", CMDOPT_NONE, APIID_GETPOSITIONINFO, ParseGetPositionInfo, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}
#endif

static HRESULT ParseGetModemType(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD *pdwModemType;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwModemType = (DWORD *)AllocBlob(sizeof(DWORD));
    if( NULL == pdwModemType)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    *pdwModemType = (DWORD)g_dwModemType;

    pBlob = pdwModemType;
    cbBlob = sizeof(DWORD);

    return hr;

Error:
    (void)FreeBlob(pdwModemType);
    return hr;
}

static HRESULT ParseGetVersionInfo(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    PRADIO_VERSION_INFO prviVersionInfo;
    DWORD dwRHAVersion;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prviVersionInfo = (PRADIO_VERSION_INFO)AllocBlob(sizeof(RADIO_VERSION_INFO));
    if( NULL == prviVersionInfo)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    (void)RHA_GetRHAVersion(&dwRHAVersion);

    prviVersionInfo->dwStructVersion = RADIO_VERSION_STRUCT_VERSION;
    prviVersionInfo->dwRILVersion = RIL_VERSION_NUM;
    prviVersionInfo->dwRHAVersion = dwRHAVersion;
    prviVersionInfo->dwAudVersion = g_dwRadioAudioProfilesVersion;
    prviVersionInfo->dwMuxVersion = GetMuxVersion();
    prviVersionInfo->dwUSBDrvVersion =  GetDllVersionInfo(TEXT("HcUsbDrv.dll"), NULL, 0);
    prviVersionInfo->dwNDISAPIVersion =  GetDllVersionInfo(TEXT("HcCmdApi.dll"), NULL, 0);

    pBlob = prviVersionInfo;
    cbBlob = sizeof(RADIO_VERSION_INFO);

    return hr;

Error:
    (void)FreeBlob(prviVersionInfo);
    return hr;
}

//
//
//
static HRESULT ParseGetProviderListDate(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    char szProviderInfo[PROVIDERLIST_DATE_STRING_LEN];
    int nLen = 0;
    LPSTR pszRsp = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    if (!GetProviderListInfo(szProviderInfo)) {
       (void)strncpyz(szProviderInfo, "unknown", PROVIDERLIST_DATE_STRING_LEN);
    }
    nLen = min(strlen(szProviderInfo) + 1, PROVIDERLIST_DATE_STRING_LEN);

    pszRsp = (LPSTR)AllocBlob(nLen);
    if (!pszRsp) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pszRsp, 0x00, nLen);
    strncpy(pszRsp, szProviderInfo, nLen);

    pBlob = (void*)pszRsp;
    cbBlob = nLen;

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

#if defined (IOCTL_RIL_GETANTENNASTATUS)
//
//
//
HRESULT RILDrv_GetAntennaStatus(DWORD dwParam)
{
    return E_NOTIMPL;
}
#endif

//
//
//
static HRESULT ParseATCResponse(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nLen;
    LPSTR pszRsp = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    nLen = strlen(szRsp);

    pszRsp = (LPSTR)AllocBlob(nLen);
    if (!pszRsp) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pszRsp, 0x00, nLen);
    strncpy(pszRsp, szRsp, nLen);

    (void)ParseRspPostfix(szRsp, szRsp);

    pBlob = (void*)pszRsp;
    cbBlob = nLen;

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

static HRESULT ParseATCFailureResponse(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    WCHAR wszText[MAX_RESOURCE_STRING_LEN];
    char szRsp[MAX_RESOURCE_STRING_LEN];
    UINT nLen;
    LPSTR pszRsp = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    if (LoadString((HINSTANCE)hInstance, (UINT)pParam, wszText, MAX_RESOURCE_STRING_LEN) == 0) {
        hr = E_INVALIDARG;
        goto Error;
    }

    (void)_snprintfz(szRsp, MAX_RESOURCE_STRING_LEN, "%S", wszText);

    nLen = strlen(szRsp);

    pszRsp = (LPSTR)AllocBlob(nLen);
    if (!pszRsp) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pszRsp, 0x00, nLen);
    strncpy(pszRsp, szRsp, nLen);

    pBlob = (void*)pszRsp;
    cbBlob = nLen;

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

static HRESULT ParseATCResponseError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    UINT nLen;
    LPSTR pszRsp = NULL;
    HRESULT hr = S_OK;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL) {
        hr = RIL_E_RADIOOFF;
        goto Error;
    }

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

    pBlob = NULL;
    cbBlob = 0;

    nLen = strlen(szRsp);

    pszRsp = (LPSTR)AllocBlob(nLen);
    if (!pszRsp) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pszRsp, 0x00, nLen);
    strncpy(pszRsp, szRsp, nLen);

    (void)ParseRspPostfix(szRsp, szRsp);

    pBlob = (void*)pszRsp;
    cbBlob = nLen;
    dwCode = RIL_RESULT_OK;

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

#if defined(NEED_OWN_PHNET_PIN)
//
//
//
static HRESULT ParseATCPreCheck(CRilHandle* /*pHandle*/, COM_PORT_TYPE /*iPort*/)
{
    // This function will only be called, if the AT+CPIN command is sent. this command must
    // be blocked, if the workaround concerning the PH-NET PIN is used.
    if (NEED_OWN_PHNET_PIN && (g_dwSIMLockedState == RIL_LOCKEDSTATE_PH_NET_PIN || g_dwSIMLockedState == RIL_LOCKEDSTATE_UNKNOWN))
        return RIL_E_OPNOTALLOWED;

    return S_OK;
}
#endif

//
//
//
HRESULT ParseGet2G3GSelection(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    UINT *puiDiag;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+WS46: <network><postfix>" or "<prefix><network><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp))  {
        hr = E_FAIL;
        goto Error;
    }
    (void)MatchStringBeginning(szRsp, "+WS46: ", szRsp);    // +WS46: shows up only on some HC25 firmware versions.
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    switch (nValue)
    {
        case 12:
           g_n2G3GSelection = SELECTION2G3G_GSM_ONLY;
            break;
        case 22:
           g_n2G3GSelection = SELECTION2G3G_UMTS_ONLY;
            break;
        case 25:
           g_n2G3GSelection = SELECTION2G3G_AUTOMATIC;
            break;		
        case 28:
           g_n2G3GSelection = SELECTION2G3G_LTE_ONLY;
		   break;
        default:
           g_n2G3GSelection = SELECTION2G3G_UNKNOWN;
    }

    *puiDiag = g_n2G3GSelection;
    pBlob = (void*)puiDiag;
    cbBlob = sizeof(UINT);

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

#ifdef SUPPORTS_EHSX_R1
//
//
//
HRESULT ParseGet2G3GSelectionXRAT(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nAct, nPAct;
    UINT *puiDiag;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+XRAT: <AcT>,<PrefAct><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+XRAT: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nAct, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nPAct, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    switch (nAct)
    {
        case 0:
           g_n2G3GSelection = SELECTION2G3G_GSM_ONLY;
            break;
        case 1:
           g_n2G3GSelection = SELECTION2G3G_AUTOMATIC;
            break;
        case 2:
           g_n2G3GSelection = SELECTION2G3G_UMTS_ONLY;
            break;	
		case 3:
		case 4:
        case 28:
           g_n2G3GSelection = SELECTION2G3G_LTE_ONLY;
        default:
           g_n2G3GSelection = SELECTION2G3G_UNKNOWN;
    }

    *puiDiag = g_n2G3GSelection;
    pBlob = (void*)puiDiag;
    cbBlob = sizeof(UINT);

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

//
//
//
HRESULT ParseGet2G3GSelectionIntel(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nAct, nPAct;
    UINT *puiDiag;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

	if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SXRAT: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nAct, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nPAct, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
	
	if(nAct == 6)
	{
	    if(!MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nPAct, szRsp))
		{
		    hr = E_FAIL;
            goto Error;
		}
	}
	if(!ParseRspPostfix(szRsp, szRsp))
	{
	    hr = E_FAIL;
        goto Error;
	}
	

    switch (nAct)
    {
        case 0:
           g_n2G3GSelection = SELECTION2G3G_GSM_ONLY;
            break;
        case 1:
		case 4:
		case 5:
		case 6:
           g_n2G3GSelection = SELECTION2G3G_AUTOMATIC;
            break;
        case 2:
           g_n2G3GSelection = SELECTION2G3G_UMTS_ONLY;
            break;		
        case 3:
		case 28:
           g_n2G3GSelection = SELECTION2G3G_LTE_ONLY;
		   break;
        default:
           g_n2G3GSelection = SELECTION2G3G_UNKNOWN;
    }

    *puiDiag = g_n2G3GSelection;
    pBlob = (void*)puiDiag;
    cbBlob = sizeof(UINT);

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

//
//
//
HRESULT ParseGet2G3GSelectionSCFG(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nBand;
    UINT *puiDiag;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>^SCFG Radio/Band,<Band><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SCFG: \"Radio/Band\",\"", szRsp) ||
        !ParseUInt(szRsp, TRUE, nBand, szRsp) ||
        !MatchStringAnywhere(szRsp, "\r\n", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if ((nBand&15) != 0  && (nBand&496) != 0) {
       g_n2G3GSelection = SELECTION2G3G_AUTOMATIC;
    }
    else if ((nBand&496) != 0) {
       g_n2G3GSelection = SELECTION2G3G_UMTS_ONLY;
    }
    else if ((nBand&15) != 0) {
       g_n2G3GSelection = SELECTION2G3G_GSM_ONLY;
    }
    else {
       g_n2G3GSelection = SELECTION2G3G_UNKNOWN;
    }

    *puiDiag = g_n2G3GSelection;
    pBlob = (void*)puiDiag;
    cbBlob = sizeof(UINT);

    if (pParam != NULL) {
        UINT *pBand = (UINT*)pParam;

        *pBand = nBand;
    }

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

//
//
//
static HRESULT ParseGetNSMCStatus(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    CRilHandle *pDevice;
    DWORD *pdwNSMC;
    HRESULT hr = S_OK;

    pDevice = (CRilHandle *)pParam;

    pBlob = NULL;
    cbBlob = 0;

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

    *pdwNSMC = (DWORD)pDevice->GetCurrentENSSetting();
    pBlob = (void*)pdwNSMC;
    cbBlob = sizeof(DWORD);

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

//
//
//
static HRESULT ParseGetSCSLPIN(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    char szPIN[MAXLEN_PIN];
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    if (pParam == NULL)
        goto Error;

    // Parse "<prefix>^SCSL: <PIN><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringAnywhere(szRsp, "^SCSL: ", szRsp)              ||
        !ParseUnquotedString(szRsp, '\r', szPIN, MAXLEN_PIN, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // In case of any problems, the keywork "ERROR" will appear instead of the PIN
    if (strcmp(szPIN, "ERROR") == 0) {
        hr = E_FAIL;
        goto Error;
    }

    (void)strncpyz((char *)pParam, szPIN, MAXLEN_PIN);

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetPhoneLockPIN(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    char szPIN[MAXLEN_PIN];
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    if (pParam == NULL)
        goto Error;

    // Parse "<prefix>+CLCK: <PIN><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                               ||
        !MatchStringAnywhere(szRsp, "+CLCK: ", szRsp)              ||
        !ParseUnquotedString(szRsp, '\r', szPIN, MAXLEN_PIN, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // In case of any problems, the keywork "ERROR" will appear instead of the PIN
    if (strcmp(szPIN, "ERROR") == 0) {
        hr = E_FAIL;
        goto Error;
    }

    (void)strncpyz((char *)pParam, szPIN, MAXLEN_PIN);

Error:
    return hr;
}

//
//
//
static HRESULT ParseGetRegStatusEx(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    DWORD* pdwRegStatus = NULL;
    HRESULT hr = S_OK;
    CRilHandle *pDevice;

    pDevice = (CRilHandle *)pParam;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>+CREG: <mode>,<status>,...<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))         {
        hr = E_FAIL;
        goto Error;
    }
    // Skip rest of +CREG parameters
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (NUM_REGSTATS > nValue) {
        *pdwRegStatus = g_rgdwRegStats[nValue];

    if(g_bFakeRegStatusDeniedToHome && (g_rgdwRegStats[nValue] == RIL_REGSTAT_DENIED))
        g_dwGPRSRegStatus = RIL_REGSTAT_HOME;
    } else {
        *pdwRegStatus = RIL_REGSTAT_UNKNOWN;
    }
    g_dwRegStatus = *pdwRegStatus;
    // Special check (needed for +CREG only): any "+CREG: 3" (access denied) is followed by a "+CREG: 0", which is
    // not correct. As a solution the RIL will still report "access denied" until the module has been registered to a
    // network, the SIM is removed or the module has been resetted.
    if (g_dwRegStatus == RIL_REGSTAT_DENIED && CREG0_FOLLOWS_CREG3)
        g_fRegStatus3detected = TRUE;
    if ((g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING) && g_fRegStatus3detected)
        g_dwRegStatus = RIL_REGSTAT_DENIED;
    if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
        g_fRegStatus3detected = FALSE;

    if (pDevice != NULL) {
        if (g_dwRegStatus == RIL_REGSTAT_ROAMING && pDevice->IsNoRoamingIndicator())
            g_dwRegStatus = RIL_REGSTAT_HOME;
        if (g_dwRegStatus == RIL_REGSTAT_ROAMING && g_iLastCOPSOpr != 0 && pDevice->NeedsCheckForNoRoamingNetwork() && pDevice->IsNoRoamingNetwork(g_iLastCOPSOpr))
            g_dwRegStatus = RIL_REGSTAT_HOME;
        *pdwRegStatus = g_dwRegStatus;
        if (pDevice->MustFakeUnregisterdState() && (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING|| g_dwRegStatus == RIL_REGSTAT_ATTEMPTING))
            *pdwRegStatus = RIL_REGSTAT_UNREGISTERED;
    }

    // Parse "<prefix>^SMONI: "
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SMONI: ", szRsp)) {
        goto Error;
    }

    if (*pdwRegStatus != RIL_REGSTAT_HOME && *pdwRegStatus != RIL_REGSTAT_ROAMING) {
        DWORD dwMoniState = SMONI_STATE_UNKNOWN;

        if (MatchStringAnywhere(szRsp, "SEARCH", szRsp))
            dwMoniState = SMONI_STATE_SEARCHING  ;
        else if (MatchStringAnywhere(szRsp, "LIMSRV", szRsp))
            dwMoniState = SMONI_STATE_LIMITED_SERVICE;
        if (pDevice == NULL)
            *pdwRegStatus = GetExtRegistrationState(*pdwRegStatus, dwMoniState, SIMTYPE_UNKNOWN);
        else
            *pdwRegStatus = GetExtRegistrationState(*pdwRegStatus, dwMoniState, pDevice->GetCurrentSIMType());
    }
    if (pDevice != NULL)
        pDevice->SendExtRegStatusNotification(*pdwRegStatus);

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

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

//
//
//
static HRESULT ParseGiveSIMLockPIN(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    char szPIN[MAXLEN_PIN];
    char *szPHNetPin = NULL;

    pBlob = NULL;
    cbBlob = 0;

    if (FetchSIMLockPINSecure(szPIN, MAXLEN_PIN)) {
        UINT uiLen = strlen(szPIN)+1;

        szPHNetPin = (char*)AllocBlob(uiLen);
        if (!szPHNetPin) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        (void)strncpyz(szPHNetPin, szPIN, uiLen);
        cbBlob = strlen(szPIN);
        pBlob = (void*)szPHNetPin;
    }
    else {
        hr = E_FAIL;
        goto Error;
    }

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

// AT Tunneling: some AT commands print out a "CONNECT" message even though
// they are not expecting any data. These commands need a special APIID to be
// handled correctly. Example: AT^SBNW=apgs,-1
static APIID GetAtTunnelingApiid(char *szCmd)
{
    APIID apiid = APIID_DEVSPECIFIC;

    if (strstr(szCmd, "^sbnw") != NULL || strstr(szCmd, "^SBNW") != NULL)
        apiid = APIID_DELETE_AGPS;
    return apiid;
}

//
//
//
HRESULT RILDrv_DevSpecific(
    DWORD dwParam,
    const BYTE* lpbParams,              // @parm parameters for the operation to be performed
    DWORD dwSize                        // @parm size of the data pointed to by <p lpParams> in bytes
)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    DWORD dwSelection;

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

    if (dwSize < sizeof(DWORD))
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    dwSelection = *(DWORD*)lpbParams;

    switch (dwSelection)
    {
        case RIL_DEVSPECIFIC_BANDHANDOVER:
        {
            hr = E_NOTIMPL;
        }
        break;

        case RIL_DEVSPECIFIC_GETMODEMTYPE:
        {
            // Send a noop command to pass the modem type back
            if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseGetModemType, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GETVERSIONINFO:
        {
            // Send a noop command to pass the modem type back
            if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseGetVersionInfo, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GETPROVIDERLISTDATE:
        {
            // Send a noop command to pass the provider list date back
            if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseGetProviderListDate, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_SEND_ATC:
        {
            char szCmd[MAX_ATCMD_LEN];
            APIID apiid = APIID_DEVSPECIFIC;

            if (dwSize > MAX_ATCMD_LEN || dwSize < 5)
            {
                hr = E_FAIL;
                goto Error;
            }

            memset(szCmd, 0, MAX_ATCMD_LEN);
            strncpy(szCmd, (char *)lpbParams+sizeof(DWORD), MAX_ATCMD_LEN-1);

            // Like in C++ '//' indicates a comment. So we don't execute it and just store it.
            if (strncmp(szCmd, "//", 2) == 0)
            {
                strncpy(g_szSequenceFromDialer, szCmd+2, MAX_ATCMD_LEN-2);

                // Send a noop command
                if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, NULL, NULL, hr))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
            else
            {
                PFN_PRE_CMD_CHECK pPreCheckHandle = NULL;
#if defined(NEED_OWN_PHNET_PIN)
                UINT iIdx = 0;
                char szCmdUpper[MAX_ATCMD_LEN];

                // Test, if a AT+CPIN command is sent. If this is the case it must be checked, if the
                // PH-NET workaround is currently active. In this case, the cammand must not be
                // sent. The actual check is performed in the pre-check function in case the RIL-API
                // is called during initialization, when not all information are available yet.
                memset(szCmdUpper, 0, MAX_ATCMD_LEN);
                for (iIdx = 0; iIdx < strlen(szCmd); iIdx++)
                    szCmdUpper[iIdx] = (char)toupper(szCmd[iIdx]);

                if (strstr(szCmdUpper, "+CPIN=") != NULL) {
                    pPreCheckHandle = ParseATCPreCheck;
                }
#endif

                if (szCmd[strlen(szCmd)-1] != '\r')
                    (void)strncatz(szCmd, "\r", MAX_ATCMD_LEN);

                apiid = GetAtTunnelingApiid(szCmd);

                // Send a AT command to the module and pass the result back without any actual parsing.
                if(!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, apiid, ParseATCResponse, NULL, hr, 0, 0, 0, NULL, NULL, NULL, pPreCheckHandle))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
        }
        break;

        case RIL_DEVSPECIFIC_FORCEEQSTATEOFF:
            // We have to generate an asynchronos response by sending an no operation command.
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: Call of RIL_DevSpecific(RIL_DEVSPECIFIC_FORCEEQSTATEOFF)\r\n")));
#ifdef RIL_RADIO_RESILIENCE
            g_fAllowReboot = FALSE;
            ClearPINSecure();
#endif
            ClearPIN2Secure();
            if (g_PowerManager.ForcePowerOff())
            {
                (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
            }
            else
                hr = E_FAIL;
        break;

        case RIL_DEVSPECIFIC_SET2G3GSELECTION:
        {
            UINT nSel = SELECTION2G3G_UNKNOWN;

            if (dwSize == sizeof(UINT)+sizeof(DWORD))
                nSel = *(UINT*)(lpbParams+sizeof(DWORD));

            if (nSel >= SELECTION2G3G_AUTOMATIC && nSel <= SELECTION2G3G_LTE_ONLY)
            {
            	BOOL bApplySelection = FALSE;
	
				//ELSx variant don't support CS call.
				// We must not change band while a voice / CSD call is active,
                // so we check the call state first.
				if (SUPPORTS_ONLY_DATAMODE){
					bApplySelection = TRUE;
				}else if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CLCC\r", CMDOPT_NONE, APIID_GETCALLLIST, 0, CLCC_RETRIES, CLCC_DELAY, ParseGetCallListCheck, NULL, (LPVOID)(pHandle->GetDevice()))){
                    if (g_iNrCallInfo >= 1)
                    {
                        // In case there is an active call we must not apply any UMTS/GSM changes,
                        // and return an error instead.
                        hr = RIL_E_SERVICEOPTIONTEMPORARILYOUTOFORDER;
                    }
                    else
                    {
                       bApplySelection = TRUE;
                    }
                }
                else
                    hr = E_FAIL;

				if(bApplySelection){
					// Start applying the new settings and return OK.
                    g_n2G3GSelection = nSel;
                    (void)SetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, RIL_SELECTION_2G3D, g_n2G3GSelection);
                    CloseHandle(CreateThread(NULL, 0, Check2G3GRegistration, (LPVOID)pHandle->GetDevice(), 0, 0));
                    (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
				}
            }
            else
                hr = E_INVALIDARG;
        }
        break;

        case RIL_DEVSPECIFIC_SEND_ATCEX:
        {
            char szCmd[MAX_ATCMD_LEN];
            DWORD dwOpt = ATCEX_OPT_PORT_DEFAULT;
            PFN_CMD_ERROR pErrorHandle = ParseATCResponseError;
            HCCMD_MODE iPort = HCCMD_INVALID;
            UINT uiErrorCode = 0;
            APIID apiid = APIID_DEVSPECIFIC;

            if (dwSize > MAX_ATCMD_LEN || dwSize < 5)
            {
                hr = E_FAIL;
                goto Error;
            }

            memset(szCmd, 0, MAX_ATCMD_LEN);
            if (dwSize < 9)
            {
                // The option parameter is obviously missing.
                memcpy(szCmd, (char *)lpbParams+sizeof(DWORD), dwSize);
            }
            else
            {
                if (*(lpbParams+sizeof(DWORD)) == (BYTE)'A' || *(lpbParams+sizeof(DWORD)) == (BYTE)'a')
                {
                    // The option parameter is probably missing.
                    memcpy(szCmd, (char *)lpbParams+sizeof(DWORD), dwSize);
                }
                else
                {
                    dwOpt = *(DWORD*)(lpbParams+sizeof(DWORD));
                    memcpy(szCmd, (char *)lpbParams+sizeof(DWORD)+sizeof(DWORD), dwSize-sizeof(DWORD));
                }
            }
            szCmd[MAX_ATCMD_LEN - 1] = '\0';
            if (szCmd[strlen(szCmd)-1] != '\r')
                (void)strncatz(szCmd, "\r", MAX_ATCMD_LEN);

            if (g_PowerManager.IsRadioOff())
            {
                hr = RIL_E_RADIOOFF;
                uiErrorCode = IDS_INDICATION_ATCEX_POWEROFF;
                if ((dwOpt&ATCEX_OPT_PORT_NOERRORPARSE)!=0)
                    goto Error;
            }

            switch  (dwOpt & 0x0F)
            {
                case ATCEX_OPT_PORT_DEFAULT:
                    iPort = HCCMD_STANDARD;
                    break;
                case ATCEX_OPT_PORT_URCPORT:
                    iPort = HCCMD_URCPORT_ONLY;
                    break;
                case ATCEX_OPT_PORT_DATAPORT:
                    iPort = HCCMD_DATAPORT_ONLY;
                    if (pHandle->GetDevice()->GetComDevice(DATA_PORT)->FDataMode())
                    {
                        if ((dwOpt&ATCEX_OPT_PORT_NOERRORPARSE)!=0)
                            iPort = HCCMD_INVALID;
                        else
                            uiErrorCode = IDS_INDICATION_ATCEX_DATAMODE;
                    }
                    break;
            }
            if ((dwOpt&ATCEX_OPT_PORT_NOERRORPARSE)!=0)
                pErrorHandle = NULL;

            if (iPort == HCCMD_INVALID)
            {
                hr = E_INVALIDARG;
                goto Error;
            }

            if (uiErrorCode == 0)
            {
                PFN_PRE_CMD_CHECK pPreCheckHandle = NULL;
#if defined(NEED_OWN_PHNET_PIN)
                UINT iIdx = 0;
                char szCmdUpper[MAX_ATCMD_LEN];

                // Test, if a AT+CPIN command is sent. If this is the case it must be checked, if the
                // PH-NET workaround is currently active. In this case, the cammand must not be
                // sent. The actual check is performed in the pre-check function in case the RIL-API
                // is called during initialization, when not all information are available yet.
                memset(szCmdUpper, 0, MAX_ATCMD_LEN);
                for (iIdx = 0; iIdx < strlen(szCmd); iIdx++)
                    szCmdUpper[iIdx] = (char)toupper(szCmd[iIdx]);

                if (strstr(szCmdUpper, "+CPIN=") != NULL) {
                    pPreCheckHandle = ParseATCPreCheck;
                }
#endif
                apiid = GetAtTunnelingApiid(szCmd);

                // Send a AT command to the module and pass the result back without any actual parsing.
                if(!QueueCmdWithTimeout(iPort, pHandle, szCmd, NULL, CMDOPT_NONE, apiid, ParseATCResponse, NULL, hr, 0, 0, 0, pErrorHandle, NULL, NULL, pPreCheckHandle))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
            else
            {
                // We have an error condition and generate a verbose error message
                if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseATCFailureResponse, NULL, hr, NULL, (LPVOID)uiErrorCode))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
        }
        break;

        case RIL_DEVSPECIFIC_WRITE_TO_LOG:
        {
            char szComment[MAX_ATCMD_LEN];

            if (dwSize > MAX_ATCMD_LEN-1)
            {
                hr = E_FAIL;
                goto Error;
            }

            memset(szComment, 0, MAX_ATCMD_LEN);
            memcpy(szComment, (char *)lpbParams+sizeof(DWORD), dwSize-sizeof(DWORD));
            RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv/Ext: %a\r\n"), szComment));
            if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, NULL, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_ACTIVATE_SIMLOCK:
        {
            char szCmd[MAX_ATCMD_LEN];
            char szNetwork[MAX_ATCMD_LEN];
            char szPIN[MAXLEN_PIN];
            char szPINCmd[MAXLEN_PIN];

            if (dwSize >= MAX_ATCMD_LEN-1)
            {
                hr = E_FAIL;
                goto Error;
            }

            if (!SUPPORTS_CUSTOMER_SIM_LOCK) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_OPNOTSUPPORTED))
                    hr = E_FAIL;
                goto Error;
            }

            ClearSIMLockPINSecure();
            memset(szPIN, 0, MAXLEN_PIN);
            memset(szPINCmd, 0, MAXLEN_PIN);
            memset(szNetwork, 0, MAX_ATCMD_LEN);
            memcpy(szNetwork, (char *)lpbParams+sizeof(DWORD), dwSize-sizeof(DWORD));
            if (strlen(szNetwork) == 0) {
                hr = E_INVALIDARG;
                goto Error;
            }
            if (SUPPORTS_CUSTOMER_SIM_LOCK_WITH_PUK)
                (void)strncpy(szCmd, "AT^SCSL=\"PN\",5\r", MAX_ATCMD_LEN);
            else
                (void)strncpy(szCmd, "AT^SCSL=\"PN\",4\r", MAX_ATCMD_LEN);
            if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_GETLOCKINGSTATUS, 0, 0, 0, ParseGetSCSLPIN, NULL, (LPVOID)szPINCmd)) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_OPNOTALLOWED))
                    hr = E_FAIL;
                goto Error;
            }
            if (strlen(szNetwork) == 0) {
                hr = E_INVALIDARG;
                goto Error;
            }
#if defined(NEED_OWN_PHNET_PIN)
            if (NEED_OWN_PHNET_PIN && atoi(szPINCmd) != HC25_R2_PHNET_PIN) {
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: Unexpected Customer SIM-Lock PIN from module!\r\n")));
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_OPNOTALLOWED))
                    hr = E_FAIL;
                goto Error;
            }

            if (NEED_OWN_PHNET_PIN && atoi(szPINCmd) == HC25_R2_PHNET_PIN) {
                char szIMEI[IMEI_BUFFER_LEN];

                memset(szIMEI, 0, IMEI_BUFFER_LEN);
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+GSN\r", CMDOPT_NONE, APIID_GETEQUIPMENTINFO, 0, 0, 0, ParseGetIMEIInfo, NULL, (LPVOID)szIMEI);
                if (strlen(szIMEI) == EXPECTED_IMEI_LEN) {
                    // If correct IMEI is available use this information to get the PH-NET PIN for the HC25 R2.
                    (void)_snprintfz(szPIN, MAXLEN_PIN, "%08d", GenerateHC25SIMLockPin(szIMEI));
                    // Foir the actual AT command the fixed PIN is used.
                    (void)_snprintfz(szPINCmd, MAXLEN_PIN, "%08d", HC25_R2_PHNET_PIN);
                }
                else {
                    hr = E_INVALIDARG;
                    goto Error;
                }
            }
            else
#endif
            {
                (void)strncpyz(szPIN, szPINCmd, MAXLEN_PIN);
            }
            if (SUPPORTS_CUSTOMER_SIM_LOCK_WITH_PUK) {
                char *pComma;

                // The string with the PIN might contain an additional PUK. This must be filtered.
                pComma = strstr(szPINCmd, ",");
                if (pComma != 0)
                    *pComma = '\0';
            }
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCSL=\"PN\",1,\"%s\",\"%s\"\r", szPINCmd, szNetwork);
            SaveSIMLockPINSecure(szPIN);
            if(!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_SETLOCKINGSTATUS, ParseGiveSIMLockPIN, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_ACTIVATE_PHONELOCK:
        {
            char szCmd[MAX_ATCMD_LEN];
            char szPIN[MAXLEN_PIN];

            if (dwSize > MAX_ATCMD_LEN-1)
            {
                hr = E_FAIL;
                goto Error;
            }

            if (!SUPPORTS_CUSTOMER_PHONE_LOCK) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_OPNOTSUPPORTED))
                    hr = E_FAIL;
                goto Error;
            }

            ClearSIMLockPINSecure();
            memset(szPIN, 0, MAXLEN_PIN);
            if (!QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CLCK=\"PS\",3\r", CMDOPT_NONE, APIID_GETLOCKINGSTATUS, 0, 0, 0, ParseGetPhoneLockPIN, NULL, (LPVOID)szPIN)) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_OPNOTALLOWED))
                    hr = E_FAIL;
                goto Error;
            }
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CLCK=\"PS\",1,\"%s\"\r", szPIN);
            SaveSIMLockPINSecure(szPIN);
            if(!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_SUPPRESSLOGGING, APIID_SETLOCKINGSTATUS, ParseGiveSIMLockPIN, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GET_NSMC_STATUS:
        {
            if(!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_DEVSPECIFIC, ParseGetNSMCStatus, NULL, hr, NULL, (LPVOID)pHandle->GetDevice()))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GET2G3GSELECTION:
        {
            const char *szNetCmd = "AT+WS46?\r";
            PFN_CMD_PARSE fnParsing = ParseGet2G3GSelection;

            if (INTEL_STYLE_NET_CMDS) {
                szNetCmd = "AT^SXRAT?\r";
                fnParsing = ParseGet2G3GSelectionIntel;
            }
#ifdef SUPPORTS_EHSX_R1
            if (INTEL_AT_CMDS) {
                szNetCmd = "AT+XRAT?\r";
                fnParsing = ParseGet2G3GSelectionXRAT;
            }
#endif
            if (AVOID_COPS_ACT) {
                szNetCmd = "AT^SCFG=Radio/Band\r";
                fnParsing = ParseGet2G3GSelectionSCFG;
            }
            if(!QueueCmd(HCCMD_STANDARD, pHandle, szNetCmd, CMDOPT_NONE, APIID_DEVSPECIFIC, fnParsing, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_GET_REGSTATUS_EX:
        {
            if(!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CREG?;^SMONI\r", CMDOPT_NONE, APIID_DEVSPECIFIC, ParseGetRegStatusEx, NULL, hr, NULL, (LPVOID)pHandle->GetDevice()))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFIC_MTLR_RESPONSE:
        {
            UINT nSel = MTLR_RESPONSE_DENY;

            if (dwSize == sizeof(UINT)+sizeof(DWORD))
                nSel = *(UINT*)(lpbParams+sizeof(DWORD));

            if (nSel >= MTLR_RESPONSE_DENY && nSel <= MTLR_RESPONSE_DEREGISTER)
            {
                char szCmd[MAX_ATCMD_LEN];

                (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SGPSMT=%d\r", nSel);
                if(!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_DEVSPECIFIC, NULL, NULL, hr))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
            else
                hr = E_INVALIDARG;
        }
        break;

#ifndef RIL_ENABLE_POWER_HANDLING
        case RIL_DEVSPECIFIC_DISABLEURCS:
        {
            // Disable the URCs
            (void)g_PowerManager.EnterUrcState(pHandle, DEFAULT_URCCONFIG);
            (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
        }
        break;

        case RIL_DEVSPECIFIC_ENABLEURCS:
        {
            // Enable the URCs
            (void)g_PowerManager.EnterUrcState(pHandle, MODEMSTATE_POWERON);
            (void)QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_NONE, NULL, NULL, hr);
        }
        break;
#endif // RIL_ENABLE_POWER_HANDLING

        default:
        {
            hr = E_NOTIMPL;
            goto Error;
        }
    }

Error:
    return hr;
}

static HRESULT ParseGetCurrentSystemType(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    DWORD* pdwSystemType = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    *pdwSystemType = (DWORD)pParam;

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

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

//
//
//
HRESULT RILDrv_GetCurrentSystemType(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }
    if (g_PowerManager.IsRadioOff())
    {
        DWORD dwSystemType = 0;
        BOOL bForceError = FALSE;

        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CurrentSystemTypeOff"), &dwSystemType))
            bForceError = TRUE;

#ifdef RIL_REPORT_DEFAULT_SYSTEM_TYPE
        if (bForceError) {
            dwSystemType = RIL_SYSTEMTYPE_GSM | RIL_SYSTEMTYPE_GPRS | RIL_SYSTEMTYPE_EDGE | RIL_SYSTEMTYPE_UMTS | RIL_SYSTEMTYPE_HSDPA;
            bForceError = FALSE;
        }
#endif
        if (bForceError) {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETCURRENTSYSTEMTYPE, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_RADIOOFF))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        else {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETCURRENTSYSTEMTYPE, ParseGetCurrentSystemType, NULL, hr, NULL, (LPVOID)dwSystemType))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
    }
    else
    {
        DWORD dwSystemType = 0;
        BOOL bQueryModule = FALSE;

        if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CurrentSystemTypeOn"), &dwSystemType))
            bQueryModule = TRUE;

        if (bQueryModule)
        {
#ifdef SUPPORTS_EHSX_R1
            if (NO_SIND_INDICATIONS) {
                if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+XREG?\r", CMDOPT_NONE, APIID_GETCURRENTSYSTEMTYPE, ParseInitialXREGInfo, NULL, hr))
                {
                    hr = E_FAIL;
                    goto Error;
                }
            }
            else {
#endif
                if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT^SIND=psinfo,2\r", CMDOPT_NONE, APIID_GETCURRENTSYSTEMTYPE, ParseInitialPSInfoCS, NULL, hr, ParseInitialPSInfoError, (LPVOID)pHandle->GetDevice()))
                {
                    hr = E_FAIL;
                    goto Error;
                }
#ifdef SUPPORTS_EHSX_R1
            }
#endif
        }
        else {
            if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETCURRENTSYSTEMTYPE, ParseGetCurrentSystemType, NULL, hr, NULL, (LPVOID)dwSystemType))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
    }

Error:
    return hr;
}

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

    // This is an CDMA only feature
    if (!CDMA_ONLY_RADIO && !GLOBAL_MODE_RADIO) {
        hr = E_NOTIMPL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=\"CDMA/PrivMode\",\"%u\"\r", dwPrivacyMode == RIL_CALLPRIVACY_ENHANCED ? 1 : 0);
    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETPREFERREDPRIVACYMODE, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

static HRESULT ParseGetPreferredPrivacyMode(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    DWORD* pdwPrivacyMode = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "<prefix>^SCFG: <URC>,<addressID><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                   ||
        !MatchStringBeginning(szRsp, "^SCFG: \"CDMA/PrivMode\",\"", szRsp)  ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)          ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    *pdwPrivacyMode = (nValue == 1 ? RIL_CALLPRIVACY_ENHANCED : RIL_CALLPRIVACY_STANDARD);

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

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

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

    // This is an CDMA only feature
    if (!CDMA_ONLY_RADIO && !GLOBAL_MODE_RADIO) {
        hr = E_NOTIMPL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT^SCFG=\"CDMA/PrivMode\"\r", CMDOPT_NONE, APIID_GETPREFERREDPRIVACYMODE, ParseGetPreferredPrivacyMode, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

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

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

    // This is an CDMA only feature
    if (!CDMA_ONLY_RADIO && !GLOBAL_MODE_RADIO) {
        hr = E_NOTIMPL;
        goto Error;
    }

    // There is no current/preferred privacy status in the PXS8, to the RIL
    // assumes, that the preferred mode is also the current status.
    if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT^SCFG=\"CDMA/PrivMode\"\r", CMDOPT_NONE, APIID_GETCURRENTPRIVACYSTATUS, ParseGetPreferredPrivacyMode, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


static HRESULT ParseGetCurrentLocationStatus(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD *pdwLocStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    *pdwLocStatus = RIL_LOCATION_OFF;

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

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

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

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

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_IGNORERADIOOFF|CMDOPT_NOOP, APIID_GETCURRENTPRIVACYSTATUS, ParseGetCurrentLocationStatus, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

static HRESULT ParseGetCurrentRoamingStatus(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    DWORD *pdwRoamStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

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

    *pdwRoamStatus = RIL_ROAMSTATUS_DIGITAL;

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

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

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

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

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_IGNORERADIOOFF|CMDOPT_NOOP, APIID_GETCURRENTPRIVACYSTATUS, ParseGetCurrentRoamingStatus, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

static HRESULT ParseGetRoamingMode(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT nValue;
    HRESULT hr = E_FAIL;
    DWORD *pdwRoamingMode=NULL;
    pBlob = NULL;
    cbBlob = 0;

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

    // Parse "$ROAM: "
    if (!MatchStringBeginning(szRsp, "$ROAM: ", szRsp))
    {
        goto Error;
    }

    // Parse <mode>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    pdwRoamingMode = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRoamingMode)
    {
        goto Error;
    }

    (void)FlagFromValue(nValue, g_RoamingMode, NUM_ROAMINGMODES, *pdwRoamingMode);

    pBlob = (void*)pdwRoamingMode;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

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

    return hr;
}


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

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

    if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT$ROAM?\r", CMDOPT_NONE, APIID_GETROAMINGMODE, ParseGetRoamingMode, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

HRESULT RILDrv_SetRoamingMode(DWORD dwParam, DWORD dwRoamingMode)
{
    UINT Value;
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_ATCMD_LEN];
    if (!ValueFromFlag(dwRoamingMode, g_RoamingMode, NUM_ROAMINGMODES, Value))
    {
        goto Error;
    }
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT$ROAM=%u\r",Value);


    if (!QueueCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SETROAMINGMODE,  NULL, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

