/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/util.cpp $
 * $Revision: /main/ms-d/19 $
 *     $Date: 2013-03-15 14:09:16 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/util.cpp$ 
 * 
 * /main/ms-d/19   2013-03-15 14:09:16   pmackent 
 * Start introducing Java update for EHS5
 *
 * /main/ms-d/18   2012-10-08 16:18:14   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/17   2012-02-10 17:21:00   ctna0256
 * Solved problems with phonebook entries type ASCII and usage of EXT2
 *
 * /main/ms-d/16   2012-02-09 16:44:19   ctna0256
 * Check for missed SMS with AT+CRSM on EHS5, small bug fixes
 *
 * /main/ms-d/15   2012-01-05 10:26:56   ctna0256
 * Save PIN2 because of problem with AT+CLCK="FD" in PH8 Rel2 (IPIS100076746)
 *
 * /main/ms-d/14   2011-09-26 13:35:31   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/13   2010-11-26 10:22:27   ctna0256
 * Fix bug in FindString() function
 *
 * /main/ms-d/12   2010-07-02 11:05:26   ctna0256
 * New handling of Customer SIM lock for HC25 R2 (IPIS100065826)
 *
 * /main/ms-d/11   2010-02-03 10:51:21   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/10   2009-07-13 09:34:57   ctna0256
 * Removed unnecessary extern declaration
 *
 * /main/ms-d/9   2008-09-18 15:21:51   ctna0256
 * Utility function to delete trailing commas
 *
 * /main/ms-d/8   2008-08-26 14:27:03   ctna0256
 * Merged RIL_RADIO_RESILIENCE option from Classic-RIL
 *
 * /main/ms-d/7   2008-08-22 10:28:20   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/6   2008-08-21 09:35:44   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/5   2008-06-20 11:05:34   mp49210
 * Remove unused SIEMENS_MAGNETO define
 *
 * /main/ms-d/4   2008-04-03 14:42:04   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/3   2007-12-05 13:56:59   mp49210
 * Proper use of debug prints
 *
 * /main/ms-d/2   2006-11-13 10:05:05   mp49210
 * New PIN2 handling in Quinn
 *
 * /main/ms-d/1   2006-10-25 16:52:28   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"
#ifndef NO_STATSTORE
#include "statstor.h"
#endif // NO_STATSTORE

//
// Table used to convert from GSM default alphabet to Unicode
//
static const WCHAR g_rgwchGSMToUnicode[128] =
{
    L'@',   0x00a3, L'$',   0x00a5, 0x00e8, 0x00e9, 0x00f9, 0x00ec, // 0x00 - 0x07
    0x00f2, 0x00c7, L'\n',  0x00d8, 0x00f8,  L'\r', 0x00c5, 0x00e5, // 0x08 - 0x0f
    0x0394, L'_',   0x03a6, 0x0393, 0x039b, 0x03a9, 0x03a0, 0x03a8, // 0x10 - 0x17
    0x03a3, 0x0398, 0x039e, L' ',   0x00c6, 0x00e6, 0x00df, 0x00c9, // 0x18 - 0x1f
    L' ',   L'!',   L'"',   L'#',   0x00a4, L'%',   L'&',   L'\'',  // 0x20 - 0x27
    L'(',   L')',   L'*',   L'+',   L',',   L'-',   L'.',   L'/',   // 0x28 - 0x2f
    L'0',   L'1',   L'2',   L'3',   L'4',   L'5',   L'6',   L'7',   // 0x30 - 0x37
    L'8',   L'9',   L':',   L';',   L'<',   L'=',   L'>',   L'?',   // 0x38 - 0x3f
    0x00a1, L'A',   L'B',   L'C',   L'D',   L'E',   L'F',   L'G',   // 0x40 - 0x47
    L'H',   L'I',   L'J',   L'K',   L'L',   L'M',   L'N',   L'O',   // 0x48 - 0x4f
    L'P',   L'Q',   L'R',   L'S',   L'T',   L'U',   L'V',   L'W',   // 0x50 - 0x57
    L'X',   L'Y',   L'Z',   0x00c4, 0x00d6, 0x00d1, 0x00dc, 0x00a7, // 0x58 - 0x5f
    0x00bf, L'a',   L'b',   L'c',   L'd',   L'e',   L'f',   L'g',   // 0x60 - 0x67
    L'h',   L'i',   L'j',   L'k',   L'l',   L'm',   L'n',   L'o',   // 0x68 - 0x6f
    L'p',   L'q',   L'r',   L's',   L't',   L'u',   L'v',   L'w',   // 0x70 - 0x77
    L'x',   L'y',   L'z',   0x00e4, 0x00f6, 0x00f1, 0x00fc, 0x00e0, // 0x78 - 0x7f
};


//
// Table used to convert from Unicode to GSM default alphabet
//
static const CHARMAP g_rgcmUnicodeToGSMExceptions[] =
{
    { 0x02, L'$'}, { 0x00, L'@'}, { 0x11, L'_'}, { 0x40, 0x00a1},
    { 0x01, 0x00a3}, { 0x24, 0x00a4}, { 0x03, 0x00a5}, { 0x5f, 0x00a7},
    { 0x60, 0x00bf}, { 0x41, 0x00c0}, { 0x41, 0x00c1}, { 0x41, 0x00c2},
    { 0x41, 0x00c3}, { 0x5b, 0x00c4}, { 0x0e, 0x00c5}, { 0x1c, 0x00c6},
    { 0x09, 0x00c7}, { 0x45, 0x00c8}, { 0x1f, 0x00c9}, { 0x45, 0x00ca},
    { 0x45, 0x00cb}, { 0x49, 0x00cc}, { 0x49, 0x00cd}, { 0x49, 0x00ce},
    { 0x49, 0x00cf}, { 0x5d, 0x00d1}, { 0x4f, 0x00d2}, { 0x4f, 0x00d3},
    { 0x4f, 0x00d4}, { 0x4f, 0x00d5}, { 0x5c, 0x00d6}, { 0x0b, 0x00d8},
    { 0x55, 0x00d9}, { 0x55, 0x00da}, { 0x55, 0x00db}, { 0x5e, 0x00dc},
    { 0x59, 0x00dd}, { 0x1e, 0x00df}, { 0x7f, 0x00e0}, { 0x61, 0x00e1},
    { 0x61, 0x00e2}, { 0x61, 0x00e3}, { 0x7b, 0x00e4}, { 0x0f, 0x00e5},
    { 0x1d, 0x00e6}, { 0x09, 0x00e7}, { 0x04, 0x00e8}, { 0x05, 0x00e9},
    { 0x65, 0x00ea}, { 0x65, 0x00eb}, { 0x07, 0x00ec}, { 0x69, 0x00ed},
    { 0x69, 0x00ee}, { 0x69, 0x00ef}, { 0x7d, 0x00f1}, { 0x08, 0x00f2},
    { 0x6f, 0x00f3}, { 0x6f, 0x00f4}, { 0x6f, 0x00f5}, { 0x7c, 0x00f6},
    { 0x0c, 0x00f8}, { 0x06, 0x00f9}, { 0x75, 0x00fa}, { 0x75, 0x00fb},
    { 0x7e, 0x00fc}, { 0x79, 0x00fd}, { 0x79, 0x00ff}, { 0x13, 0x0393},
    { 0x10, 0x0394}, { 0x19, 0x0398}, { 0x14, 0x039b}, { 0x1a, 0x039e},
    { 0x16, 0x03a0}, { 0x18, 0x03a3}, { 0x12, 0x03a6}, { 0x17, 0x03a8},
    { 0x15, 0x03a9},
};
#define NUM_EXCEPTIONS  (sizeof(g_rgcmUnicodeToGSMExceptions) / sizeof(CHARMAP))

//
// Table used to map semi-byte values to hex characters
//
static const char g_rgchSemiByteToCharMap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

extern DWORD g_TimeoutCmdInit;

//
// Delete trailing commas
//
char *DeleteTrailingCommas(char *szEnd, char *szStart)
{
    char *szWalk = szEnd;
    while (*(szWalk-1) == ',' && szWalk > szStart)
    {
        szWalk--;
        *szWalk = '\0';
    }
    return szWalk;
}


//
// Convert a semi-byte into its character representation
//
char SemiByteToChar(const BYTE bByte, const BOOL fHigh)
{
    BYTE bSemiByte = (fHigh ? (bByte & 0xf0) >> 4 : bByte & 0x0f);
    return g_rgchSemiByteToCharMap[bSemiByte];
}


//
// Combine 2 characters representing semi-bytes into a byte
//
BYTE SemiByteCharsToByte(const char chHigh, const char chLow)
{
    BYTE bRet;

    if ('0' <= chHigh && '9' >= chHigh)
    {
        bRet = (chHigh - '0') << 4;
    }
    else
    {
        bRet = (0x0a + chHigh - 'A') << 4;
    }

    if ('0' <= chLow && '9' >= chLow)
    {
        bRet |= (chLow - '0');
    }
    else
    {
        bRet |= (0x0a + chLow - 'A');
    }
    return bRet;
}


//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareChars(const void* pElem1, const void* pElem2)
{
    WCHAR wch1 = ((CHARMAP*)pElem1)->wch;
    WCHAR wch2 = ((CHARMAP*)pElem2)->wch;
    int iRet;

    if (wch1 < wch2)
    {
        iRet = -1;
    }
    else if (wch1 == wch2)
    {
        iRet = 0;
    }
    else
    {
        iRet = 1;
    }
    return iRet;
}


//
//
//
BOOL FindString(DWORD dwStartPos, const char *pszString1, const char *pszString2, UINT uiLen, PDWORD pdwEndPos)
{
  DWORD i, j, dwEnd;

  if (pdwEndPos) {
    *pdwEndPos = 0;
  }

  dwEnd = (DWORD)uiLen + dwStartPos;
  for (j = 0, i = dwStartPos; i < dwEnd; i++) {
    if (pszString1[i] == pszString2[j]) {
      j++;
    } else {
      j = 0;
      if (pszString1[i] == pszString2[0])
        j = 1;
    }
    if (j == strlen(pszString2)) {
      break;
    }
  }

  if (i != dwEnd) {
    if (pdwEndPos) {
      *pdwEndPos = i + 1;
    }
    return TRUE;
  }

  return FALSE;
}

void ExtRILRetailTraceZone(DWORD dwZone, LPCTSTR szText, LPCTSTR szIn, LPCSTR szCrLf)
{
    if (dwZone & g_dwDebugZones)
        TraceCRLFString(TEXT("RilDrv: "), szText, szIn, szCrLf);
}

//
// Convert a single Unicode character to GSM default alphabet
//
BOOL UnicodeCharToGSM(const WCHAR wch, char *pchRet)
{
    // Does this char map directly to Unicode?
    if (0x000a == wch ||
        0x000d == wch ||
        (0x0020 <= wch && 0x0023 >= wch) ||
        (0x0025 <= wch && 0x003f >= wch) ||
        (0x0041 <= wch && 0x005a >= wch) ||
        (0x0061 <= wch && 0x007a >= wch))
    {
        *pchRet = (char)(wch & 0x007f);
        return TRUE;
    }
    else
    {
        CHARMAP cmKey = { '\0', wch};
        CHARMAP* pcmFound;

        pcmFound = (CHARMAP*)bsearch(&cmKey, g_rgcmUnicodeToGSMExceptions, NUM_EXCEPTIONS, sizeof(CHARMAP), BSCompareChars);
        if (pcmFound)
        {
            *pchRet = pcmFound->ch;
            return TRUE;
        }
    }
    return FALSE;
}

BOOL GSMCharToUnicode(const char ch, WCHAR *pwchRet)
{
    *pwchRet = g_rgwchGSMToUnicode[ch];
    return TRUE;
}

//
//
//
BOOL GSMHexToGSM(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed)
{
    LPCSTR pchIn = sIn;
    LPCSTR pchInEnd = sIn + cbIn;
    LPSTR pchOut = sOut;
    LPSTR pchOutEnd = sOut + cbOut;
    BOOL fRet = FALSE;

    while (pchIn < pchInEnd - 1 && pchOut < pchOutEnd)
    {
        *pchOut++ = SemiByteCharsToByte(*pchIn, *(pchIn + 1));
        pchIn += 2;
    }

    rcbUsed = pchOut - sOut;
    fRet = TRUE;
    return fRet;
}


//
//
//
BOOL GSMToGSMHex(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed)
{
    LPCSTR pchIn = sIn;
    LPCSTR pchInEnd = sIn + cbIn;
    LPSTR pchOut = sOut;
    LPSTR pchOutEnd = sOut + cbOut;
    BOOL fRet = FALSE;

    while (pchIn < pchInEnd && pchOut < pchOutEnd - 1)
    {
        *pchOut = g_rgchSemiByteToCharMap[((*pchIn) & 0xf0) >> 4];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[(*pchIn) & 0x0f];
        pchOut++;

        pchIn++;
    }

    rcbUsed = pchOut - sOut;
    fRet = TRUE;
    return fRet;
}

//
//
//
BOOL UCS2ToGSMHex(const LPWSTR pwszIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut)
{
    LPWSTR pwchIn = pwszIn;
    LPWSTR pwchInEnd = pwszIn + cbIn;
    LPSTR pchOut = sOut;
    LPSTR pchOutEnd = sOut + cbOut;
    BOOL fRet = FALSE;
    WCHAR wcTemp;

    while (pwchIn < pwchInEnd && pchOut < pchOutEnd - 1)
    {
        wcTemp = *pwchIn++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0xF000) >> 12];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0x0F00) >> 8];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0x00F0) >> 4];
        pchOut++;
        *pchOut = g_rgchSemiByteToCharMap[((wcTemp) & 0x000F)];
        pchOut++;
    }
    *pchOut++ = 0;

    fRet = TRUE;
    return fRet;
}

//
// Convert a specified string from Unicode
//
#pragma warning( push )
// Warning disabled to keep code align with reference RIL.
#pragma warning(disable : 4244)
BOOL ConvertFromUnicode(const ENCODING_TYPE enc, const LPCWSTR wsIn, const UINT cchIn, const LPSTR sOut, const UINT cbOut,
                        UINT& rcbUsed)
{
    LPCWSTR pwchInEnd;
    LPCWSTR pwchIn;
    LPSTR pchOutEnd;
    LPSTR pchOut;
    LPSTR sOutTemp = NULL;
    LPSTR pchOutTemp;
    UINT cbOutUsed;
    char ch7Bit;
    UINT nBits = 0;
    BOOL fRet = FALSE;

    rcbUsed = 0;

    if (ENCODING_GSMDEFAULT == enc)
    {
        pwchInEnd = wsIn + cchIn;
        pchOutEnd = sOut + cbOut;
        pwchIn = wsIn;
        pchOut = sOut;

        while (pwchIn < pwchInEnd && pchOut < pchOutEnd)
        {
            if (!nBits)
            {
                if (!UnicodeCharToGSM(*pwchIn++,pchOut))
                {
                    *pchOut = '?';
                }
            }
            else
            {
                if (!UnicodeCharToGSM(*pwchIn++,&ch7Bit))
                {
                    ch7Bit = '?';
                }
                *pchOut++ += ch7Bit << (8 - nBits);
                *pchOut    = ch7Bit >> nBits;
            }
            nBits = (nBits + 1) % 8;
        }

        if (!nBits && pchOut > sOut)
        {
            // We incremented the destination pointer once too many
            pchOut--;
        }

        // Do various tricks to distinguish fill bits from real characters (GSM 03.38, ver. 6.0.1, chap. 6.1.2.3.1)
        // NOTE: <CR> in GSM default alphabet maps directly to Unicode <CR>
        if (7 == cchIn % 8 && !(*pchOut & 0xfe))
        {
            // We have 7 fill 0-bits at the end -- need to replace them with <CR>
            *pchOut &= 0x01;
            *pchOut |= ('\r' << 1);
        }
        else if (!(cchIn % 8) && ('\r' << 1) == *pchOut && pchOut < pchOutEnd - 1)
        {
            // We have no fill bits and the last character in the text is <CR> --
            //    we need to add another <CR>, so that terminating <CR> isn't confused with fill bits
            pchOut++;
            *pchOut = '\r' & 0x7f;
        }

        rcbUsed = (pchOut - sOut) + 1;
        fRet = TRUE;
    }
    else if (ENCODING_GSMDEFAULT_HEX == enc)
    {
        sOutTemp = new char[(cbOut / 2) + 1];
        if (!sOutTemp)
        {
            goto Error;
        }
        if (!ConvertFromUnicode(ENCODING_GSMDEFAULT, wsIn, cchIn, sOutTemp, cbOut / 2 + 1, cbOutUsed))
        {
            goto Error;
        }

        pchOutTemp = sOutTemp;
        pchOut = sOut;
        pchOutEnd = sOut + cbOut;

        while (pchOut <= pchOutEnd)
        {
            *pchOut++ = SemiByteToChar(*pchOutTemp, TRUE);
            *pchOut++ = SemiByteToChar(*pchOutTemp++, FALSE);
        }
        fRet = TRUE;
    }
    else if (ENCODING_UCS2 == enc)
    {
        for (UINT i = 0; (rcbUsed < (cbOut - 4)) && (i < cchIn) && wsIn[i]; i++, rcbUsed += 4)
        {
            (void)_snprintf(&(sOut[rcbUsed]), 4, "%0.4X", wsIn[i]);
        }
        if (rcbUsed < cbOut)
        {
            sOut[rcbUsed] = '\0';
            rcbUsed++;
        }
        fRet = TRUE;
    }
    else if (ENCODING_ASCII == enc)
    {
        pwchInEnd = wsIn + cchIn;
        pchOutEnd = sOut + cbOut;
        pwchIn = wsIn;
        pchOut = sOut;

        while (pwchIn < pwchInEnd && pchOut < pchOutEnd)
        {
            if (!UnicodeCharToGSM(*pwchIn++,pchOut))
            {
                *pchOut = '?';
            }
            pchOut++;
        }
        *pchOut = '\0';
        fRet = TRUE;
    }

    Error:
    delete[] sOutTemp;
    return fRet;
}


//
// Convert a specified string to Unicode
//
BOOL ConvertToUnicode(const ENCODING_TYPE enc, const LPCSTR sIn, const UINT cbIn, const LPWSTR wsOut, const UINT cchOut,
                      UINT& rcchUsed)
{
    LPCSTR pchInEnd;
    LPCSTR pchIn;
    LPWSTR pwchOutEnd;
    LPWSTR pwchOut;
    LPSTR sInTemp = NULL;
    UINT cbUsedTemp;
    char ch7Bit;
    UINT nBits;
    BYTE bValue;
    BOOL fRet = FALSE;

    rcchUsed = 0;

    if (ENCODING_GSMDEFAULT == enc)
    {
        pchInEnd = sIn + cbIn;
        pwchOutEnd = wsOut + cchOut;
        pchIn = sIn;
        pwchOut = wsOut;
        ch7Bit = 0x00;
        nBits = 0;

        while (pchIn < pchInEnd && pwchOut < pwchOutEnd)
        {
            bValue = (BYTE)*pchIn++;

            ch7Bit += (bValue << nBits) & 0x7f;
            *pwchOut++ = g_rgwchGSMToUnicode[ch7Bit];

            ch7Bit = (bValue >> (7 - nBits)) & 0x7f;
            nBits = (nBits + 1) % 7;
            if (!nBits && pwchOut < pwchOutEnd)
            {
                *pwchOut++ = g_rgwchGSMToUnicode[ch7Bit];
                ch7Bit = 0x00;
            }
        }

        rcchUsed = pwchOut - wsOut;
        fRet = TRUE;
    }
    else if (ENCODING_GSMDEFAULT_HEX == enc)
    {
        sInTemp = new char[cbIn / 2 + 1];
        if (!sInTemp)
        {
            goto Error;
        }

        if (!GSMHexToGSM(sIn, cbIn, sInTemp, cbIn / 2 + 1, cbUsedTemp))
        {
            goto Error;
        }
        if (!ConvertToUnicode(ENCODING_GSMDEFAULT, sInTemp, cbUsedTemp, wsOut, cchOut, rcchUsed))
        {
            goto Error;
        }
        fRet = TRUE;
    }

    Error:
    delete[] sInTemp;
    return fRet;
}
#pragma warning( pop )


//
// Convert a RILADDRESS structure to AT address string
//
HRESULT RILAddressToString(const RILADDRESS& rraAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbTypeOfAddress)
{
    UINT cbUsed;
    HRESULT hr = E_INVALIDARG;

    // Must have a type and an address specified
    if (!(rraAddress.dwParams & RIL_PARAM_A_TYPE) || !(rraAddress.dwParams & RIL_PARAM_A_ADDRESS))
    {
        goto Error;
    }

    if (rraAddress.dwType >= NUM_ADDRTYPES)
    {
        goto Error;
    }

    if (RIL_ADDRTYPE_ALPHANUM == rraAddress.dwType)
    {
        // Need to convert to default GSM alphabet
        if (!ConvertFromUnicode(ENCODING_GSMDEFAULT_HEX,
                                rraAddress.wszAddress,
                                wcslen(rraAddress.wszAddress),
                                szOut, cbOut - 1, cbUsed))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        // Just copy the number directly
        cbUsed = min(cbOut - 1, wcslen(rraAddress.wszAddress));
        strncpy(szOut, USAsciiString(rraAddress.wszAddress), cbUsed);
    }
    szOut[cbUsed] = '\0';

    // Add address type to Type-of-Address byte (high bit is always set)
    rbTypeOfAddress = 0x80 | (g_rgbAddrTypes[rraAddress.dwType] << 4);

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Must have a numbering plan specified for these address types
        if (!(rraAddress.dwParams & RIL_PARAM_A_NUMPLAN))
        {
            goto Error;
        }

        if (rraAddress.dwNumPlan >= NUM_NUMPLANS)
        {
            goto Error;
        }

        // Add numbering plan to the Type-of-addres byte
        rbTypeOfAddress |= g_rgbNumPlans[rraAddress.dwNumPlan];
    }
    else
    {
        goto Error;
    }

    hr=S_OK;
    Error:
    return hr;
}

//
// Convert a RILADDRESS structure to a UCS2 AT address string
//
HRESULT RILAddressToUCS2String(const RILADDRESS& rraAddress, const LPWSTR wszOut, const UINT cbOut, BYTE& rbTypeOfAddress)
{
    HRESULT hr = E_INVALIDARG;

    // Must have a type and an address specified
    if (!(rraAddress.dwParams & RIL_PARAM_A_TYPE) || !(rraAddress.dwParams & RIL_PARAM_A_ADDRESS))
    {
        goto Error;
    }

    if (rraAddress.dwType >= NUM_ADDRTYPES)
    {
        goto Error;
    }

    (void)wcsncpyz(wszOut, rraAddress.wszAddress, cbOut);

    // Add address type to Type-of-Address byte (high bit is always set)
    rbTypeOfAddress = 0x80 | (g_rgbAddrTypes[rraAddress.dwType] << 4);

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Must have a numbering plan specified for these address types
        if (!(rraAddress.dwParams & RIL_PARAM_A_NUMPLAN))
        {
            goto Error;
        }

        if (rraAddress.dwNumPlan >= NUM_NUMPLANS)
        {
            goto Error;
        }

        // Add numbering plan to the Type-of-addres byte
        rbTypeOfAddress |= g_rgbNumPlans[rraAddress.dwNumPlan];
    }
    else
    {
        goto Error;
    }

    hr=S_OK;
    Error:
    return hr;
}

//
// Convert an AT address string to RILADDRESS structure
//
BOOL StringToRILAddress(const LPCSTR szAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress)
{
    BYTE bType = (bTypeOfAddress & 0x70) >> 4;
    BYTE bNumPlan = bTypeOfAddress & 0x0f;
    UINT cchUsed;
    UINT i;
    BOOL fRet = FALSE;
    LPCSTR sz;

    rraAddress.cbSize = sizeof(RILADDRESS);
    rraAddress.dwParams = 0;

    // Determine the type
    for (i = 0; i < NUM_ADDRTYPES; i++)
    {
        if (g_rgbAddrTypes[i] == bType)
        {
            rraAddress.dwType = i;
            break;
        }
    }
    if (i < NUM_ADDRTYPES)
    {
        rraAddress.dwParams |= RIL_PARAM_A_TYPE;
    }

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Determine the numbering plan
        for (i = 0; i < NUM_NUMPLANS; i++)
        {
            if (g_rgbNumPlans[i] == bNumPlan)
            {
                rraAddress.dwNumPlan = i;
                break;
            }
        }
        if (i < NUM_NUMPLANS)
        {
            rraAddress.dwParams |= RIL_PARAM_A_NUMPLAN;
        }
    }

    // Convert the address string to Unicode
    sz = szAddress;

    // In case we have a '+' in a number that we already
    // know is an INTERNATIONAL type, then remove it.
    if (RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType &&
        '+' == *sz)
    {
        sz++;
    }

    // Just copy the number directly
    cchUsed = min(MAXLENGTH_ADDRESS - 1, strlen(sz));
    (void)wcsncpy(rraAddress.wszAddress, WideString(sz), cchUsed);
    rraAddress.wszAddress[cchUsed] = L'\0';
    rraAddress.dwParams |= RIL_PARAM_A_ADDRESS;
    fRet = TRUE;

    return fRet;
}

//
// Convert a UCS2 AT address string to RILADDRESS structure
//
BOOL UCS2StringToRILAddress(const LPWSTR wszAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress)
{
    BYTE bType = (bTypeOfAddress & 0x70) >> 4;
    BYTE bNumPlan = bTypeOfAddress & 0x0f;
    UINT i;
    BOOL fRet = FALSE;

    rraAddress.cbSize = sizeof(RILADDRESS);
    rraAddress.dwParams = 0;

    // Determine the type
    for (i = 0; i < NUM_ADDRTYPES; i++)
    {
        if (g_rgbAddrTypes[i] == bType)
        {
            rraAddress.dwType = i;
            break;
        }
    }
    if (i < NUM_ADDRTYPES)
    {
        rraAddress.dwParams |= RIL_PARAM_A_TYPE;
    }

    if (RIL_ADDRTYPE_UNKNOWN == rraAddress.dwType ||
        RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType ||
        RIL_ADDRTYPE_NATIONAL == rraAddress.dwType)
    {
        // Determine the numbering plan
        for (i = 0; i < NUM_NUMPLANS; i++)
        {
            if (g_rgbNumPlans[i] == bNumPlan)
            {
                rraAddress.dwNumPlan = i;
                break;
            }
        }
        if (i < NUM_NUMPLANS)
        {
            rraAddress.dwParams |= RIL_PARAM_A_NUMPLAN;
        }
    }

    LPWSTR wsz = wszAddress;

    // In case we have a '+' in a number that we already
    // know is an INTERNATIONAL type, then remove it.
    if (RIL_ADDRTYPE_INTERNATIONAL == rraAddress.dwType &&
        '+' == *wsz)
    {
        wsz++;
    }

    // Just copy the number directly
    (void)wcsncpyz(rraAddress.wszAddress, wsz, MAXLENGTH_ADDRESS);

    rraAddress.dwParams |= RIL_PARAM_A_ADDRESS;
    fRet = TRUE;

//    Error:
    return fRet;
}

//
// Convert a RILSUBADDRESS structure to AT sub-address string
//
/*HRESULT RILSubAddressToString(const RILSUBADDRESS& rrsaSubAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbType)
{
    HRESULT hr = S_OK;

    // Must have a type and a sub-address specified
    if (!(rrsaSubAddress.dwParams & RIL_PARAM_SA_TYPE) || !(rrsaSubAddress.dwParams & RIL_PARAM_SA_SUBADDRESS))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Copy the subaddress data out of the structure.
    (void)strncpyz(szOut, USAsciiString(rrsaSubAddress.wszSubAddress), cbOut);

    rbType = (BYTE)rrsaSubAddress.dwType;

    Error:
    return hr;
}*/


//
// Convert an AT sub-address string to RILSUBADDRESS structure
//
BOOL StringToRILSubAddress(const LPCSTR szSubAddress, const BYTE bType, RILSUBADDRESS& rrsaSubAddress)
{
    rrsaSubAddress.cbSize = sizeof(RILSUBADDRESS);
    rrsaSubAddress.dwParams = 0;

    // Copy the subaddress data into the structure.
    (void)wcsncpyz(rrsaSubAddress.wszSubAddress, WideString(szSubAddress), MAXLENGTH_SUBADDR);
    rrsaSubAddress.dwParams |= RIL_PARAM_SA_SUBADDRESS;

    rrsaSubAddress.dwType = bType;
    rrsaSubAddress.dwParams |= RIL_PARAM_SA_TYPE;
    return TRUE;
}


//
// Determine the error from a SendRestrictedSimCmd based on status word 1 and status word 2
//
HRESULT DetermineSimResponseError(DWORD dwSW1, DWORD dwSW2)
{
    HRESULT hr;

    switch (dwSW1)
    {
    case 0x90:
    case 0x91:
    case 0x9F:
        // This is good
        hr = S_OK;
        break;

    case 0x92:
        if ((dwSW2 & 0xf0) == 0x00)
        {
            // The command completed OK, but the SIM had to retry the command internally
            // a few times, but what do I care if the SIM is whining?
            hr = S_OK;
        }
        else
        {
            // Memory problem
            hr = RIL_E_MEMORYFULL;
        }
        break;

    case 0x94:
        // SW2 = 0x00, 0x02, or 0x08
        // These all means that there was a problem with the file (e.g. invalid address, or
        // they specified the type of file incorrectly)
        hr = RIL_E_INVALIDINDEX;
        break;

    case 0x98:
        // Security and/or authentication problems
        hr = RIL_E_SECURITYFAILURE;
        break;

    case 0x6F:
        // Internal SIM error, no further information given
        hr = RIL_E_SIMFAILURE;
        break;

    case 0x67:
    case 0x6B:
    case 0x6D:
    case 0x6E:
        // I really shouldn't be getting these -- it means that one of the parameters I sent
        // down to the SIM was invalid, but I already error checked for those -- so I guess
        // it's best to assert and let a tester yell at me.  In other words, FALL THROUGH!
        default:
        // I don't recognize this command!
        hr = E_FAIL;
        break;
    }

    return hr;
}

void UpdateStatStore()
{
#ifndef NO_STATSTORE
#ifdef RIL_UTIL_DEBUG_TRACE
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: StatStore = %x\r\n"), g_dwReadyState));
#endif
    IStatStore *pss;
    DWORD dwStatStore = 0;

    // Now, update StatStor
    if (SUCCEEDED(GetStatStore(&pss)))
    {
        if (g_dwReadyState & RIL_READYSTATE_INITIALIZED)
        {
            dwStatStore |= RRSF_READYSTATE_INITIALIZED;
        }
        if (g_dwReadyState & RIL_READYSTATE_SIM)
        {
            dwStatStore |= RRSF_READYSTATE_SIM;
        }
        if (g_dwReadyState & RIL_READYSTATE_SMS)
        {
            dwStatStore |= RRSF_READYSTATE_SMS;
        }
        if (g_dwReadyState & RIL_READYSTATE_UNLOCKED)
        {
            dwStatStore |= RRSF_READYSTATE_UNLOCKED;
        }
        if (g_dwReadyState & RIL_READYSTATE_SIM_PB)
        {
            dwStatStore |= RRSF_READYSTATE_SIM_PB;
        }
        (void)pss->UpdateUINT(SDID_RADIO_READY_STATE, FALSE, dwStatStore, RRSF_READYSTATE_ALL);
        (void)pss->Release();
    }
#endif // NO_STATSTORE
}

//
// Update the SIM state
//
void UpdateSIMState(DWORD dwSimState)
{
#ifdef RIL_UTIL_DEBUG_TRACE
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: SimState = %x\r\n"), dwSimState));
    RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: StatStore = %x\r\n"), g_dwReadyState));
#endif
#ifndef NO_STATSTORE
    IStatStore *pss;
    DWORD dwStatStoreSimState = 0;
#endif
    DWORD dwSimStatusChanged = 0;

    // Set this in our global
    switch(dwSimState)
    {
        case RIL_E_SIMPUKREQUIRED:
        case RIL_E_PHFSIMPUKREQUIRED:
        case RIL_E_NETWKPUKREQUIRED:
        case RIL_E_SUBSETPUKREQUIRED:
        case RIL_E_SVCPUKREQUIRED:
        case RIL_E_CORPPUKREQUIRED:
        case RIL_LOCKEDSTATE_SIM_PUK:
        case RIL_LOCKEDSTATE_PH_FSIM_PUK:
        case RIL_LOCKEDSTATE_PH_NET_PUK:
        case RIL_LOCKEDSTATE_PH_NETSUB_PUK:
        case RIL_LOCKEDSTATE_PH_SP_PUK:
        case RIL_LOCKEDSTATE_PH_CORP_PUK:
#ifndef NO_STATSTORE
            dwStatStoreSimState = PSF_BLOCKED_SIM;
#endif
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_BLOCKED;
           break;

        case RIL_E_SIMWRONG:
#ifndef NO_STATSTORE
            dwStatStoreSimState = PSF_INVALID_SIM;
#endif
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_INVALID;
            break;

        case RIL_E_SIMNOTINSERTED:
#ifndef NO_STATSTORE
            dwStatStoreSimState = PSF_NO_SIM;
#endif
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_NO_SIM;
            break;

        case RIL_E_MEMORYFULL:
#ifndef NO_STATSTORE
            dwStatStoreSimState = PSF_SIM_FULL;
#endif
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_FULL;
            break;

        case RIL_E_SIMPINREQUIRED:
        case RIL_E_PHSIMPINREQUIRED:
        case RIL_E_PHFSIMPINREQUIRED:
        case RIL_E_NETWKPINREQUIRED:
        case RIL_E_SUBSETPINREQUIRED:
        case RIL_E_SVCPINREQUIRED:
        case RIL_E_CORPPINREQUIRED:
        case RIL_E_RADIOOFF:
        case RIL_LOCKEDSTATE_SIM_PIN:
        case RIL_LOCKEDSTATE_PH_SIM_PIN:
        case RIL_LOCKEDSTATE_PH_FSIM_PIN:
        case RIL_LOCKEDSTATE_PH_NET_PIN:
        case RIL_LOCKEDSTATE_PH_NETSUB_PIN:
        case RIL_LOCKEDSTATE_PH_SP_PIN:
        case RIL_LOCKEDSTATE_PH_CORP_PIN:
        case RIL_LOCKEDSTATE_READY:
            // not puk'd, invalid, no sim, or full
#ifndef NO_STATSTORE
            dwStatStoreSimState = 0;
#endif
            dwSimStatusChanged = RIL_SIMSTATUSCHANGED_NONE;
            break;

        case RIL_E_SIMFAILURE:
        case RIL_E_SIMBUSY:
        default:
            // who knows.  could still be puk'd, invalid, no sim, or full
            goto Exit;
            break;
    }

#ifndef NO_STATSTORE
    if (SUCCEEDED(GetStatStore(&pss)))
    {
        (void)pss->UpdateUINT(SDID_PHONE_STATUS_FLAGS, FALSE, dwStatStoreSimState, PSF_SIM);
        (void)pss->Release();
    }
#endif // NO_STATSTORE

#ifdef RILDRV_CE6
    if (dwSimState != RIL_LOCKEDSTATE_UNKNOWN && !FAILED(dwSimState))
    {
        CNotificationData* pnd = new CNotificationData;
        RILSIMSECURITYSTATUS rsss;

        memset( &rsss, 0, sizeof(rsss) );
        rsss.cbSize = sizeof(rsss);
        rsss.dwLockedState = dwSimState;
        if (dwSimState == RIL_LOCKEDSTATE_READY)
            rsss.dwSimSecurityState = RIL_SIMSECURITYSTATE_PINRECEIVED;
        else
            rsss.dwSimSecurityState = RIL_SIMSECURITYSTATE_PINREQUESTED;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_SIMSECURITYSTATUS, (void*)&rsss, sizeof(rsss)))
        {
            delete pnd;
            pnd = NULL;
        }
        else
        {
            // Add notification to command queue.  pnd will be deleted after notification is sent.
            (void)QueueInternalCmd(HCCMD_STANDARD, NULL, NULL, CMDOPT_INIT | CMDOPT_NOOP | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, 0, 0, NULL, NULL, NULL, pnd);
        }
    }
#endif

    // Broadcast a SIM state change notification
    CNotificationData* pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_SIMSTATUSCHANGED, (void*)&dwSimStatusChanged, sizeof(dwSimStatusChanged)))
    {
        delete pnd;
        pnd = NULL;
    }
    else
    {
        // Add notification to command queue.  pnd will be deleted after notification is sent.
        (void)QueueInternalCmd(HCCMD_STANDARD, NULL, NULL, CMDOPT_INIT | CMDOPT_NOOP | CMDOPT_IGNORERSP, APIID_NONE, g_TimeoutCmdInit, 0, 0, NULL, NULL, NULL, pnd);
    }

Exit:
    ;

    if (dwSimState >= RIL_LOCKEDSTATE_UNKNOWN && dwSimState <= RIL_LOCKEDSTATE_PH_CORP_PUK &&
        dwSimState != RIL_LOCKEDSTATE_SIM_PIN2 && dwSimState != RIL_LOCKEDSTATE_SIM_PUK2)
        g_dwSIMLockedState = dwSimState;
}

//
// Creates a cmd string that includes a byte array (in HEX representation)
// NOTE: the caller is responsible for releasing memory allocated for rszCmd
//
BOOL ComposeCmdWithByteArray(const LPCSTR szPrefix, const BYTE* const pbBytes, const UINT cbBytes,
                             const LPCSTR szPostfix, LPSTR& rszCmd)
{
    UINT i;
    UINT cbCmd;
    LPSTR szWalk;
    BYTE* pbBytesWalk;
    BOOL fRet = FALSE;

    rszCmd = 0;

    // Allocate the command string
    cbCmd = MAX_ATCMD_LEN + strlen(szPrefix) + strlen(szPostfix) + cbBytes * 2;
    rszCmd = new char[cbCmd];
    if (!rszCmd)
    {
        goto Error;
    }
    szWalk = rszCmd;

    // Add "<prefix>"
    (void)strncpyz(szWalk, szPrefix, cbCmd - (szWalk - rszCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

    // Add the byte array coverted to HEX representation, if provided
    if (cbBytes)
    {
        pbBytesWalk = (BYTE*)pbBytes;
        for (i = 0; i < cbBytes; i++)
        {
            *szWalk++ = SemiByteToChar(*pbBytesWalk,   TRUE);
            *szWalk++ = SemiByteToChar(*pbBytesWalk++, FALSE);
        }
    }

    // Add "<postfix>"
    (void)strncpyz(szWalk, szPostfix, cbCmd - (szWalk - rszCmd));  // NO_TYPO: 30
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete[] rszCmd;
    }
    return fRet;
}



//
// (Re)Allocates storage for data elements
//      prgrData - Pointer to current buffer (buffer must be NULL if (0 == nAllocated))
//        stSize - Size (in bytes) of the data elements in the buffer
//         nUsed - Number of valid data elements in the buffer
//   pnAllocated - Total number of data elements in the buffer
//     nGrowSize - Number of elements by which to increase the size of the buffer
//
BOOL AllocateOrReallocateStorage(BYTE** const prgrData, const size_t stSize, const UINT nUsed, UINT* const pnAllocated, const UINT nGrowSize)
{
    BOOL fSuccess = FALSE;
    *pnAllocated += nGrowSize;
    const size_t stAllocateBytes = *pnAllocated * stSize;
    BYTE* const prbTemp = (BYTE*)AllocBlob(stAllocateBytes);
    if (prbTemp)
    {
        const size_t stUsedBytes = nUsed * stSize;
        memcpy(prbTemp, *prgrData, stUsedBytes);
        // The following line seems like a good idea, but may waste resources
        // 0-initializing memory that is never used.
        // memset(prbTemp + stUsedBytes, 0x00, stAllocateBytes - stUsedBytes);
        (void)FreeBlob(*prgrData);
        *prgrData = prbTemp;
        fSuccess = TRUE;
    }
    else
    {
        *pnAllocated -= nGrowSize;
    }
    return fSuccess;
}


//
// Buffer ctor
//
CBuffer::CBuffer()
: m_szData(NULL),
m_cbLength(0),
m_cbAlloc(0)
{
}


//
// Buffer dtor
//
CBuffer::~CBuffer()
{
    delete[] m_szData;
}


//
// Append data to the response
//
BOOL CBuffer::Append(const LPCSTR szString, const UINT cbString)
{
    LPSTR szTemp = NULL;
    BOOL fRet = FALSE;

    if (!m_szData)
    {
        m_cbLength = 0;
        m_cbAlloc = ALLOC_SIZE;
        m_szData = new char[m_cbAlloc];
        if (!m_szData)
        {
            goto Error;
        }
    }

    if (m_cbLength + cbString + 1 > m_cbAlloc)
    {
        do
        {
            m_cbAlloc += ALLOC_SIZE;
        } while (m_cbLength + cbString + 1 > m_cbAlloc);

        szTemp = new char[m_cbAlloc];
        if (!szTemp)
        {
            goto Error;
        }

        memcpy(szTemp, m_szData, m_cbLength);
        delete[] m_szData;
        m_szData = szTemp;
    }

    memcpy(m_szData + m_cbLength, szString, cbString);
    m_cbLength += cbString;
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Get the data out of the buffer
//    (the caller takes ownership of the m_szData storage)
//
LPSTR CBuffer::GiveUpData(void)
{
    LPSTR szTemp = m_szData;

    m_szData = NULL;
    m_cbLength = 0;
    m_cbAlloc = 0;

    return szTemp;
}


//
//
//
void CBuffer::InheritData(CBuffer* pSrcBuffer)
{
    delete[] m_szData;
    m_szData = NULL;
    m_cbLength = 0;
    m_cbAlloc = 0;

    m_cbAlloc = pSrcBuffer->m_cbAlloc;
    m_cbLength = pSrcBuffer->m_cbLength;
    m_szData = pSrcBuffer->GiveUpData();
}



//
// Shared resource ctor
//
CSharedResource::CSharedResource()
: m_fInited(FALSE),
m_dwSharedUsers(0),
m_hSharedUseSph(NULL),
m_hExclusiveUseSph(NULL),
m_hCancelEvent(NULL)
{
    InitializeCriticalSection(&m_cs);
}


//
// Shared resource dtor
//
CSharedResource::~CSharedResource()
{
    if (m_hSharedUseSph)
    {
        (void)CloseHandle(m_hSharedUseSph);
        m_hSharedUseSph = NULL;
    }

    if (m_hExclusiveUseSph)
    {
        (void)CloseHandle(m_hExclusiveUseSph);
        m_hExclusiveUseSph = NULL;
    }

    DeleteCriticalSection(&m_cs);
    m_fInited = FALSE;
}


//
// Shared resource intialization
//
BOOL CSharedResource::Init(const HANDLE hCancelEvent)
{
    if (m_fInited)
    {
        goto Error;
    }

    m_hSharedUseSph = CreateSemaphore(NULL, 1, 1, NULL);
    if (!m_hSharedUseSph)
    {
        goto Error;
    }

    m_hExclusiveUseSph = CreateSemaphore(NULL, 1, 1, NULL);
    if (!m_hExclusiveUseSph)
    {
        goto Error;
    }

    m_hCancelEvent = hCancelEvent;
    m_fInited = TRUE;

    Error:
    return m_fInited;
}


//
// Called to acquire shared use permission
//
BOOL CSharedResource::EnterSharedUse()
{
    HANDLE rghObjects[2] = { m_hSharedUseSph, m_hCancelEvent};
    DWORD dwWait;
    BOOL fRet = FALSE;

    if (!m_fInited)
    {
        goto Error;
    }

    // Wait until shared use is granted
    dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, INFINITE);
    if (WAIT_OBJECT_0 + 1 == dwWait)
    {
        // We hit the terminate event -- quit
        goto Error;
    }
    else if (WAIT_OBJECT_0 != dwWait)
    {
        goto Error;
    }

    {
        SYNCBLOCK(m_cs);

        // Grant shared use for anyone who's waiting
        if (!ReleaseSemaphore(m_hSharedUseSph, 1, NULL))
        {
            goto Error;
        }

        // Increment the number of shared users
        m_dwSharedUsers++;

        // If this is the first shared user, we need to make sure there are no exclusive users at the moment
        if (1 == m_dwSharedUsers)
        {
            // Wait until the exclusive user is done
            rghObjects[0] = m_hExclusiveUseSph;
            dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, INFINITE);
            if (WAIT_OBJECT_0 + 1 == dwWait)
            {
                // We hit the terminate event -- quit
                goto Error;
            }
            else if (WAIT_OBJECT_0 != dwWait)
            {
                goto Error;
            }
        }
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Called to relinquish shared use permission
//
BOOL CSharedResource::ExitSharedUse()
{
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    if (!m_fInited)
    {
        goto Error;
    }

    // Decrement the number of shared users
    m_dwSharedUsers--;

    // If this is the last shared user, allow exclusive use
    if (!m_dwSharedUsers)
    {
        if (!ReleaseSemaphore(m_hExclusiveUseSph, 1, NULL))
        {
            goto Error;
        }
    }

    fRet = TRUE;

    Error:
    return fRet;
}


//
// Called to acquire exclusive use permission
//
BOOL CSharedResource::EnterExclusiveUse(PFN_SHRDUSE_ACTION pfnAction, const DWORD dwParam) const
{
    HANDLE rghObjects[2] = { m_hSharedUseSph, m_hCancelEvent};
    DWORD dwWait;
    BOOL fRet = FALSE;
    int RetryCount = 10;

    if (!m_fInited)
    {
        goto Error;
    }

    // Lock shared users out until this exclusive user can finish its operation
    dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, INFINITE);
    if (WAIT_OBJECT_0 + 1 == dwWait)
    {
        // We hit the terminate event -- quit
        goto Error;
    }
    else if (WAIT_OBJECT_0 != dwWait)
    {
        goto Error;
    }

    for (;;)
    {
        // Call the custom action routine, passing it the specified parameter
        pfnAction(dwParam);

        // Wait until there are no other exclusive users
        rghObjects[0] = m_hExclusiveUseSph;
        dwWait = WaitForMultipleObjects(2, rghObjects, FALSE, 500);
        if (WAIT_OBJECT_0 == dwWait)
        {
            // We own it now.
            break;
        }
        if (WAIT_OBJECT_0 + 1 == dwWait)
        {
            // We hit the terminate event -- quit
            goto Error;
        }
        else if (WAIT_TIMEOUT == dwWait)
        {
            // We timed out.
            // Have we exceeded our retry count?
            if (--RetryCount<=0)
            {
                goto Error;
            }
            // Try to signal and wait again.
        }
        else
        {
            // Some other error
            goto Error;
        }
    }

    // Allow shared users to proceed (they will be locked out until there're no more exclusive users)
    if (!ReleaseSemaphore(m_hSharedUseSph, 1, NULL))
    {
        goto Error;
    }
    fRet = TRUE;

    Error:
    return fRet;
}


//
// Called to relinquish exclusive use permission
//
BOOL CSharedResource::ExitExclusiveUse(void) const
{
    // Allow other exclusive users and shared users to proceed
    return ReleaseSemaphore(m_hExclusiveUseSph, 1, NULL);
}

BYTE g_ClearSIMLockPINSecurePINCRYPTBUFFER[256];
DWORD g_cbClearSIMLockPINSecurePINBytes=0;

void ClearSIMLockPINSecure()
{
    g_cbClearSIMLockPINSecurePINBytes=0;
}

void SaveSIMLockPINSecure(LPCSTR szNewPIN)
{
    DATA_BLOB inBlob;
    inBlob.pbData = (BYTE*)szNewPIN;
    if (szNewPIN == NULL)
        inBlob.cbData = 0;
    else
        inBlob.cbData = strlen(szNewPIN) + 1;
    DATA_BLOB outBlob;
    outBlob.cbData = sizeof(g_ClearSIMLockPINSecurePINCRYPTBUFFER);
    outBlob.pbData = g_ClearSIMLockPINSecurePINCRYPTBUFFER;

    if ((szNewPIN == NULL) || (*szNewPIN == '\0') || (CryptProtectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        g_cbClearSIMLockPINSecurePINBytes = 0;
    }
    else
    {
        g_cbClearSIMLockPINSecurePINBytes = outBlob.cbData;
    }
}

BOOL FetchSIMLockPINSecure(LPSTR szPinBuffer, DWORD cbBufferSIze)
{
    DATA_BLOB inBlob;
    inBlob.cbData = g_cbClearSIMLockPINSecurePINBytes;
    inBlob.pbData = g_ClearSIMLockPINSecurePINCRYPTBUFFER;
    DATA_BLOB outBlob;
    outBlob.cbData = cbBufferSIze;
    outBlob.pbData = (BYTE *)szPinBuffer;
    BOOL fReturn = FALSE;

    if ((g_cbClearSIMLockPINSecurePINBytes == 0) || (CryptUnprotectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        szPinBuffer[0] = '\0';
    }
    else
    {
        fReturn = TRUE;
    }
    return fReturn;
}

#ifdef RIL_RADIO_RESILIENCE
BYTE g_PINCRYPTBUFFER[256];
DWORD g_cbPINBytes=0;

void ClearPINSecure()
{
    g_cbPINBytes=0;
}

void SavePINSecure(LPCSTR szNewPIN)
{
    DATA_BLOB inBlob;
    inBlob.pbData = (BYTE*)szNewPIN;
    if (szNewPIN == NULL)
        inBlob.cbData = 0;
    else
        inBlob.cbData = strlen(szNewPIN) + 1;
    DATA_BLOB outBlob;
    outBlob.cbData = sizeof(g_PINCRYPTBUFFER);
    outBlob.pbData = g_PINCRYPTBUFFER;

    if ((szNewPIN == NULL) || (*szNewPIN == '\0') || (CryptProtectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        g_cbPINBytes = 0;
    }
    else
    {
        g_cbPINBytes = outBlob.cbData;
    }
}

BOOL FetchPINSecure(LPSTR szPinBuffer, DWORD cbBufferSIze)
{
    DATA_BLOB inBlob;
    inBlob.cbData = g_cbPINBytes;
    inBlob.pbData = g_PINCRYPTBUFFER;
    DATA_BLOB outBlob;
    outBlob.cbData = cbBufferSIze;
    outBlob.pbData = (BYTE *)szPinBuffer;
    BOOL fReturn = FALSE;

    if ((g_cbPINBytes == 0) || (CryptUnprotectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        szPinBuffer[0] = '\0';
    }
    else
    {
        fReturn = TRUE;
    }
    return fReturn;
}
#endif

BYTE g_PIN2CRYPTBUFFER[256];
DWORD g_cbPIN2Bytes=0;

void ClearPIN2Secure()
{
    g_cbPIN2Bytes=0;
}

void SavePIN2Secure(LPCSTR szNewPIN2)
{
    DATA_BLOB inBlob;
    inBlob.pbData = (BYTE*)szNewPIN2;
    if (szNewPIN2 == NULL)
        inBlob.cbData = 0;
    else
        inBlob.cbData = strlen(szNewPIN2) + 1;
    DATA_BLOB outBlob;
    outBlob.cbData = sizeof(g_PIN2CRYPTBUFFER);
    outBlob.pbData = g_PIN2CRYPTBUFFER;

    if ((szNewPIN2 == NULL) || (*szNewPIN2 == '\0') || (CryptProtectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        g_cbPIN2Bytes = 0;
    }
    else
    {
        g_cbPIN2Bytes = outBlob.cbData;
    }
}

BOOL FetchPIN2Secure(LPSTR szPinBuffer, DWORD cbBufferSIze)
{
    DATA_BLOB inBlob;
    inBlob.cbData = g_cbPIN2Bytes;
    inBlob.pbData = g_PIN2CRYPTBUFFER;
    DATA_BLOB outBlob;
    outBlob.cbData = cbBufferSIze;
    outBlob.pbData = (BYTE *)szPinBuffer;
    BOOL fReturn = FALSE;

    if ((g_cbPIN2Bytes == 0) || (CryptUnprotectData(&inBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_SYSTEM, &outBlob) == FALSE))
    {
        szPinBuffer[0] = '\0';
    }
    else
    {
        fReturn = TRUE;
    }
    return fReturn;
}

static const unsigned long crc_table[256] = {
  0x00000000L,0x77073096L,0xee0e612cL,0x990951baL,0x076dc419L,0x706af48fL,0xe963a535L,0x9e6495a3L,
  0x0edb8832L,0x79dcb8a4L,0xe0d5e91eL,0x97d2d988L,0x09b64c2bL,0x7eb17cbdL,0xe7b82d07L,0x90bf1d91L,
  0x1db71064L,0x6ab020f2L,0xf3b97148L,0x84be41deL,0x1adad47dL,0x6ddde4ebL,0xf4d4b551L,0x83d385c7L,
  0x136c9856L,0x646ba8c0L,0xfd62f97aL,0x8a65c9ecL,0x14015c4fL,0x63066cd9L,0xfa0f3d63L,0x8d080df5L,
  0x3b6e20c8L,0x4c69105eL,0xd56041e4L,0xa2677172L,0x3c03e4d1L,0x4b04d447L,0xd20d85fdL,0xa50ab56bL,
  0x35b5a8faL,0x42b2986cL,0xdbbbc9d6L,0xacbcf940L,0x32d86ce3L,0x45df5c75L,0xdcd60dcfL,0xabd13d59L,
  0x26d930acL,0x51de003aL,0xc8d75180L,0xbfd06116L,0x21b4f4b5L,0x56b3c423L,0xcfba9599L,0xb8bda50fL,
  0x2802b89eL,0x5f058808L,0xc60cd9b2L,0xb10be924L,0x2f6f7c87L,0x58684c11L,0xc1611dabL,0xb6662d3dL,
  0x76dc4190L,0x01db7106L,0x98d220bcL,0xefd5102aL,0x71b18589L,0x06b6b51fL,0x9fbfe4a5L,0xe8b8d433L,
  0x7807c9a2L,0x0f00f934L,0x9609a88eL,0xe10e9818L,0x7f6a0dbbL,0x086d3d2dL,0x91646c97L,0xe6635c01L,
  0x6b6b51f4L,0x1c6c6162L,0x856530d8L,0xf262004eL,0x6c0695edL,0x1b01a57bL,0x8208f4c1L,0xf50fc457L,
  0x65b0d9c6L,0x12b7e950L,0x8bbeb8eaL,0xfcb9887cL,0x62dd1ddfL,0x15da2d49L,0x8cd37cf3L,0xfbd44c65L,
  0x4db26158L,0x3ab551ceL,0xa3bc0074L,0xd4bb30e2L,0x4adfa541L,0x3dd895d7L,0xa4d1c46dL,0xd3d6f4fbL,
  0x4369e96aL,0x346ed9fcL,0xad678846L,0xda60b8d0L,0x44042d73L,0x33031de5L,0xaa0a4c5fL,0xdd0d7cc9L,
  0x5005713cL,0x270241aaL,0xbe0b1010L,0xc90c2086L,0x5768b525L,0x206f85b3L,0xb966d409L,0xce61e49fL,
  0x5edef90eL,0x29d9c998L,0xb0d09822L,0xc7d7a8b4L,0x59b33d17L,0x2eb40d81L,0xb7bd5c3bL,0xc0ba6cadL,
  0xedb88320L,0x9abfb3b6L,0x03b6e20cL,0x74b1d29aL,0xead54739L,0x9dd277afL,0x04db2615L,0x73dc1683L,
  0xe3630b12L,0x94643b84L,0x0d6d6a3eL,0x7a6a5aa8L,0xe40ecf0bL,0x9309ff9dL,0x0a00ae27L,0x7d079eb1L,
  0xf00f9344L,0x8708a3d2L,0x1e01f268L,0x6906c2feL,0xf762575dL,0x806567cbL,0x196c3671L,0x6e6b06e7L,
  0xfed41b76L,0x89d32be0L,0x10da7a5aL,0x67dd4accL,0xf9b9df6fL,0x8ebeeff9L,0x17b7be43L,0x60b08ed5L,
  0xd6d6a3e8L,0xa1d1937eL,0x38d8c2c4L,0x4fdff252L,0xd1bb67f1L,0xa6bc5767L,0x3fb506ddL,0x48b2364bL,
  0xd80d2bdaL,0xaf0a1b4cL,0x36034af6L,0x41047a60L,0xdf60efc3L,0xa867df55L,0x316e8eefL,0x4669be79L,
  0xcb61b38cL,0xbc66831aL,0x256fd2a0L,0x5268e236L,0xcc0c7795L,0xbb0b4703L,0x220216b9L,0x5505262fL,
  0xc5ba3bbeL,0xb2bd0b28L,0x2bb45a92L,0x5cb36a04L,0xc2d7ffa7L,0xb5d0cf31L,0x2cd99e8bL,0x5bdeae1dL,
  0x9b64c2b0L,0xec63f226L,0x756aa39cL,0x026d930aL,0x9c0906a9L,0xeb0e363fL,0x72076785L,0x05005713L,
  0x95bf4a82L,0xe2b87a14L,0x7bb12baeL,0x0cb61b38L,0x92d28e9bL,0xe5d5be0dL,0x7cdcefb7L,0x0bdbdf21L,
  0x86d3d2d4L,0xf1d4e242L,0x68ddb3f8L,0x1fda836eL,0x81be16cdL,0xf6b9265bL,0x6fb077e1L,0x18b74777L,
  0x88085ae6L,0xff0f6a70L,0x66063bcaL,0x11010b5cL,0x8f659effL,0xf862ae69L,0x616bffd3L,0x166ccf45L,
  0xa00ae278L,0xd70dd2eeL,0x4e048354L,0x3903b3c2L,0xa7672661L,0xd06016f7L,0x4969474dL,0x3e6e77dbL,
  0xaed16a4aL,0xd9d65adcL,0x40df0b66L,0x37d83bf0L,0xa9bcae53L,0xdebb9ec5L,0x47b2cf7fL,0x30b5ffe9L,
  0xbdbdf21cL,0xcabac28aL,0x53b39330L,0x24b4a3a6L,0xbad03605L,0xcdd70693L,0x54de5729L,0x23d967bfL,
  0xb3667a2eL,0xc4614ab8L,0x5d681b02L,0x2a6f2b94L,0xb40bbe37L,0xc30c8ea1L,0x5a05df1bL,0x2d02ef8dL
};

DWORD CalcCrc(BYTE *bBuffer, DWORD dwLen)
{
    UINT i;
    DWORD dwCrc = 0xffffffff;

    for (i = 0; i < dwLen; i++)
        dwCrc = crc_table[(dwCrc ^ bBuffer[i]) & 0xff] ^ (dwCrc >> 8);
    dwCrc = dwCrc ^ 0xffffffff;
    return dwCrc;
}

