//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

#include <ttpcom.h>

// Phonebook storage locations - NEEDS to be SORTED

static const LISTSTRINGMAP g_rglsmCapsPBLocations[] =
{
    { "EN",   RIL_CAPS_PBLOC_SIMEMERGENCY   },
    { "FD",   RIL_CAPS_PBLOC_SIMFIXDIALING  },
    { "LD",   RIL_CAPS_PBLOC_SIMLASTDIALING },
    { "ON",   RIL_CAPS_PBLOC_OWNNUMBERS     },
    { "SM",   RIL_CAPS_PBLOC_SIMPHONEBOOK   },		// Used by OEM2 and Wavecom
};

// Phonebook storage locations - does NOT NEED to be SORTED

static const LPCSTR g_rgszPBLocations[] =
{
    "",             // RIL_PBLOC_UNKNOWN
    "EN",           // RIL_PBLOC_SIMEMERGENCY
    "FD",           // RIL_PBLOC_SIMFIXDIALING
    "LD",           // RIL_PBLOC_SIMLASTDIALING
    "ON",           // RIL_PBLOC_OWNNUMBERS
    "SM",           // RIL_PBLOC_SIMPHONEBOOK   (Used by OEM2 and Wavecom)
};

// Reboot the radio

#define IOCTL_TTPCOM_REBOOT CTL_CODE( FILE_DEVICE_SERIAL_PORT, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS)

static const ERRORMAP g_rgemCMSErrors[] =
{
    { RIL_E_PHONEFAILURE,           500},
    { RIL_E_MESSAGINGERROR,     516},
    { RIL_E_MESSAGINGERROR,     517},
};

// RIL registry key
const LPCTSTR g_tszRegKeyRIL = TEXT("Software\\Microsoft\\RIL");


// HW-SPECIFIC: The following init commands are hardware-specific
// NOTES: Z                 resets the AT interface
//        S0=0              disables auto-answer
//        E0                disables command echo
//        Q0                enable result codes
//        V0                enable non-verbose responses
//        X4                enable busy signal detection and additional info in CONNECT response
//        &C1               enables DCD to be used for carrier detection
//        &D1               enables DTR to be used for dropping out of data mode
//        +CMEE=1           enables +CME ERROR and +CMS ERROR responses
//        +CFUN=0,2         ensures radio is off and radio will not power on at module power on (OEM2 only)
//        +CRC=1            enables +CRING notifications
//        +CR=1             enables +CR notifications
//        +CMOD=0           enables single call mode
//        +CREG=1           enables short +CREG notifications
//        +CREG=2           enables extended +CREG notifications (w/ <lac> and <ci>)
//        +FCLASS=0         enables data mode
//        +CSSN=0,1         enables +CSSU notifications
//        +CHSR=1           enables +CHSR notifications
//        +COLP=1           enables +COLP notifications
//        +CCWA=1           enables +CCWA notifications
//        +CMGF=0           enables SMS PDU mode
//        +CNMI=2,2,2,1,0   enables +CMT, +CBM, and +CDS notifications
//        +CSCS="8859-1"    sets default alphabet to ISO 8859-1
//        +STRT=240",1      (OEM2 only) set the SIM toolkit command timeout in seconds. Currently this is set to match the SIMTK internal timeout 
//        +EXUNSOL="SQ",1        enables unsolicited signal quality notifications (0 = disable, 1 = enable, 2 = query) (OEM2 only)
//        +EXUNSOL="BC",1        enables unsolicited battery notifications (0 = disable, 1 = enable, 2 = query) (OEM2 only)
//        +EXUNSOL="UR",1        enables unsolicited call progress notifications (0 = disable, 1 = enable, 2 = query) (OEM2 only)

// only supports two init levels below:
//    COM_INIT_INDEX - index 0
//    COM_REINIT_INDEX - index 1

const INITSTRING_DATA g_rgisdModuleInit[] =
{
    // OEM2 init commands
    { "+CFUN=0,2|Z;S0=0;E0;Q0;V0|X3;&C1;&D1;+CMEE=1;+CRC=1;+CR=1;+CMOD=0;+CREG=1;"
      "+FCLASS=0;+CSSN=0,1;+COLP=0;+CCWA=1;+CSCS=\"UCS2\";+CMGF=0|+CGREG=1|+CUSD=1|+CMUT=0|"
    "+STRT=240|+EXUNSOL=\"SQ\",1|+EXUNSOL=\"BC\",1|+EXUNSOL=\"UR\",1|", CMDOPT_INIT | CMDOPT_GENERALINIT},    
    { "E0|+CMUT=0|", CMDOPT_INIT | CMDOPT_GENERALINIT},
};

HANDLE g_hCPINNEvent = NULL;

// This driver doesn't have a way to query current
// uplink and downlink volume, so we have to cache them.
DWORD   g_dwCacheUplinkVolume;
DWORD   g_dwCacheDownlinkVolume;

// PDD parameter table

const RILPDDParams rppTTPCOMPDDParams = {
    sizeof(rppTTPCOMPDDParams),                             // cbSize
    false,                                                  // fRadioSignonRequired
    false,                                                  // fIgnoreUnsolicitedNoCarrier
    NULL,                                                   // pfnParseGetCurrentAddressId
    NULL,                                                   // pfnParseATRInfo
    g_rglsmCapsPBLocations,                                 // plsmPhoneBookLocationCapsTable
    sizeof(g_rglsmCapsPBLocations) / sizeof(LISTSTRINGMAP), // uiPhoneBookLocationCapsTableSize
    RIL_SIMTKN_RADIOIMPLEMENTS_NOTIFICATION,                // dwDevCapsSIMTKN_SetUpCall
    RIL_SIMTKN_RADIOIMPLEMENTS_REQUESTMEINPUT,              // dwDevCapsSIMTKN_SendDTMF
    true,                                                   // fNotificationPortSupported
    RIL_SIMCMD_STATUS,                                      // dwCRSMStatusCommandId
    15,                                                     // RestrictedSimAccessSetCmdParam3 (3GPP TS 27.007 V6.8.0 (2005-03) sec. 8.18 Restricted SIM access +CRSM)
    false,                                                  // fGPRSQoSEmptyParameterListAllowed
    g_rgszPBLocations,                                      // pstrPhoneBookLocationTable
    sizeof(g_rgszPBLocations) / sizeof(LPCSTR),             // uiPhoneBookLocationTableSize
    true,                                                   // fIsRadioOffInMinimumPowerState
    true,                                                   // fDontTurnRadioOnIfAlreadyOn
    false,                                                  // fSendSimCmdSupported
    true,                                                   // fCRSMResponseQuoted
    NULL,                                                   // pfnParseGetSimToolkitProfile
    NULL,                                                   // pfnParseSendSimToolkitEnvelopeCmd
    false,                                                  // fPowerManagementSupported
    NULL,                                                   // pfnParseBuildInfo
    NULL,                                                   // pfnParseBatteryInfo
    ENCODING_GSMDEFAULT_UNICODE,                            // etMessageEncodingType
    NULL,                                                   // pfnParsePostRebootDiagnostics
    500,                                                    // dwPostRebootInitDelay
    NULL,                                                   // pemCMEErrorTable
    0,                                                      // uiCMEErrorTableSize
    g_rgemCMSErrors,                                        // pemCMSErrorTable
    sizeof(g_rgemCMSErrors) / sizeof(ERRORMAP),             // uiCMSErrorTableSize
    NULL,                                                   // pemEXTErrorTable
    0,                                                      // uiEXTErrorTableSize
    "\r\n> ",                                               // pchSMSIntermediaryPrompt
    false,                                                  // fIgnoreDeniedNotificationsUntilUnlock
    true,                                                   // fRequireWaitingForSMSReady
    true,                                                   // fRequireWaitingForSIMPhoneBookReady
    false,                                                  // fExplicitRadioReadyNotification
    true,                                                   // fSIMToolkit_SupportsTextNotifications
    true,                                                   // fSIMToolkit_SupportsTextFormat
    true,                                                   // fByteArrayIsQuoted
    false,                                                  // fSetAudioDevicesSupported
    DEFAULT_COM_MASK,                                       // dwDefaultCOMMask
    ENCODING_TECHARSET,                                     // etEncodingTECharset
    true,                                                   // fSendRegStatusChangedAfterEveryReadyStateNotification
    false,                                                  // fSignalQualityActualLevelSupported
    g_rgisdModuleInit,                                      // pisdInitStringDataTable
    sizeof(g_rgisdModuleInit) / sizeof(INITSTRING_DATA),    // uiInitStringDataTableSize
    NULL,                                                   // pfnParseGetNITZSupported
};

//Check if this is a SIM PIN notification : SIM PIN (PIN2, PUK2, PIN, PUK)

HRESULT PDD_IsSIMPINNotification(__in const void* rpBlob, __in const UINT rcbBlob, __in const DWORD dwCode, __out bool& fIsSIMPIN)
{
    HRESULT hr = E_FAIL;

    fIsSIMPIN = false;

    if( rpBlob != NULL && rcbBlob == sizeof(HRESULT) )
    {
        if ( dwCode == RIL_RESULT_ERROR )
        {
            if((RIL_E_SIMPIN2REQUIRED ==*(HRESULT*)rpBlob) ||(RIL_E_SIMPUK2REQUIRED == *(HRESULT*)rpBlob) 
                ||(RIL_E_SIMPINREQUIRED == *(HRESULT*)rpBlob) ||(RIL_E_SIMPUKREQUIRED == *(HRESULT*)rpBlob))
                fIsSIMPIN = true;
        }
        else if ( dwCode == RIL_RESULT_OK )
        {
            fIsSIMPIN = ( S_OK == *(HRESULT*)rpBlob );
        }
    }
    else
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    hr = S_OK;

Error:
    return hr;
}

// Given the current command and it's response, should the response be queued

HRESULT PDD_QueueResponse(__in CCommand* pCurrCommand, __inout CResponse* pRsp, __out bool& fQueueResponse)
{
    // For this radio, queue response always
    fQueueResponse = true;
    
	return S_OK;
}

// Create a AT command string to send the required DTMF tones
// pdwDuration and pNumVals are OUT parameters that are then used to calculate the command timeout period

HRESULT PDD_CreateCommand_SendDTMF(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in LPCSTR lpszChars, __out DWORD& dwDuration, __out UINT& uiNumVals)
{
    UINT nValue = 0;
    LPSTR szWalk = szCmd;
    HRESULT hr = E_FAIL;

    if ((dwDuration < 300) || (dwDuration == RIL_DTMFDURATION_DEFAULT))
    {
        dwDuration = 300;
    }

    nValue = (dwDuration + 50 ) / 100;

    hr = StringCchPrintfA( szWalk, cchCmd, "AT+VTD=%u;", nValue );

    if ( FAILED( hr ) )
        {
        DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : PDD_CreateCommand_SendDTMF : error in StringCchPrintfA, hr = [0x%08x]\r\n"), hr));
        goto Error;
        }

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

    uiNumVals=0;

    // OEM2 has a special version of AT+VTS which allows up to 20 DTMF tones in a single command

    LPCSTR pchChars = lpszChars;
    if (*pchChars)
    {
        UINT nSent = 0;
        uiNumVals++;

        hr = StringCchPrintfA( szWalk, cchCmd - ( szWalk - szCmd ), "+VTS=\"%c", *pchChars );

        if ( FAILED( hr ) )
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : PDD_CreateCommand_SendDTMF : error in StringCchPrintfA, hr = [0x%08x]\r\n"), hr));
            goto Error;
        }
   
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
        pchChars++;
        nSent++;

        // The length of szCmd is limited by MAX_PATH(260), thus at most 107 characters can be fit into one AT command
        for (; (*pchChars) && (pchChars-lpszChars<=107); pchChars++) 
        {
            uiNumVals++;
            if (nSent >= 20)
            {
                hr = StringCchPrintfA( szWalk, cchCmd - ( szWalk - szCmd ), "\";+VTS=\"%c", *pchChars );

                if ( FAILED( hr ) )
                {
                    DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : PDD_CreateCommand_SendDTMF : error in StringCchPrintfA, hr = [0x%08x]\r\n"), hr));
                    goto Error;
                }            
                
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
                DEBUGCHK(NULL != szWalk);
                nSent = 1;
            }
            else
            {
                hr = StringCchPrintfA( szWalk, cchCmd - ( szWalk - szCmd ), ",%c", *pchChars );

                if ( FAILED( hr ) )
                {
                    DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : PDD_CreateCommand_SendDTMF : error in StringCchPrintfA, hr = [0x%08x]\r\n"), hr));
                    goto Error;
                }     

                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
                DEBUGCHK(NULL != szWalk);
                nSent++;
            }
        }

        hr = StringCchCopyA( szWalk, cchCmd - ( szWalk - szCmd ), "\"" );

        if ( FAILED( hr ) )
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : PDD_CreateCommand_SendDTMF : error in StringCchCopyA, hr = [0x%08x]\r\n"), hr));
            goto Error;
        } 
                
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    hr = StringCchCopyA( szWalk, cchCmd - ( szWalk - szCmd ), "\r" );

    if ( FAILED( hr ) )
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : PDD_CreateCommand_SendDTMF : error in StringCchCopyA, hr = [0x%08x]\r\n"), hr));
        goto Error;
    } 

    hr = S_OK;

Error:    
    return hr;    
}

HRESULT PDD_CreateCommand_StartDTMF(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const char ch)
{
   return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_StopDTMF(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const char ch)
{
   return E_NOTIMPL;
}

// Indicate call activity to audio subsystem

HRESULT PDD_IndicateCallActivityToAudioSubsystem(__in const bool fCallIsActive)
{
   return E_NOTIMPL;
}

// Create ReleaseHeld command

HRESULT PDD_CreateCommand_ReleaseHeld(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT+CHLD=7\r" );
}

HRESULT PDD_CreateCommand_ReleaseCall(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwID)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT+CHLD=1%u\r", dwID );
}

// Create HoldActiveAcceptHeld command

HRESULT PDD_CreateCommand_HoldActiveAcceptHeld(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT+CHLD=6\r" );
}

// Create SetCurrentAddressId command

HRESULT PDD_CreateCommand_SetCurrentAddressId(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwAddressId)
{
   return E_NOTIMPL;
}

// Create GetCurrentAddressId command

HRESULT PDD_CreateCommand_GetCurrentAddressId(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return E_NOTIMPL;
}

// Prepare to power the radio on

HRESULT PDD_BeginRadioPowerOn()
{
    // On OEM2 we need to reset the CPINN event in case one has happened previously.
    if (NULL != g_hCPINNEvent)
    {
        ResetEvent(g_hCPINNEvent);
    }

    return S_OK;
}

// Prepare to deactivate GPRS context

HRESULT PDD_BeginGPRSContextDeactivation()
{
   return E_NOTIMPL;
}

// Wait for the SIM to be ready

HRESULT PDD_WaitForSIMReady()
{
    HRESULT hr = S_OK;
    
    // On OEM2 we really can't allow any other commands to go down after a CFUN=1 until we get a +CPINN notification.
    // Note that this restriction doesn't apply if the previous equipment state is RIL_EQSTATE_DISABLETX, 
    // RIL_EQSTATE_DISABLERX, or RIL_EQSTATE_DISABLETXANDRX.
    if (NULL != g_hCPINNEvent)
        {
        DWORD dwWaitResult = WaitForSingleObject(g_hCPINNEvent, 30000);
        switch (dwWaitResult)
            {
            case WAIT_OBJECT_0:
                break;
            case WAIT_TIMEOUT:
                // hr = E_TIMEOUT;
                DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : Timeout for CPINN event - this is only an error if the previous RIL_EQSTATE was RIL_EQSTATE_MINIMUM\r\n")));
                break;
            default:
                hr = E_FAIL;
                DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : Error waiting for CPINN event\n")));
                break;
            }
        }
    
    return hr;
}

// Create GetEquipmentInfo command

HRESULT PDD_CreateCommand_GetEquipmentInfo(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return StringCchCopyA( szCmd, cchCmd, "AT+CGMI;+CGMM;+CGMR;+CGSN\r" );
}

// Create command for any setup before radio on

HRESULT PDD_CreateCommand_SetupBeforeRadioOn(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const bool fPrefixPresent)
{
    return E_NOTIMPL;
}

// Create command for any cleanup before radio off

HRESULT PDD_CreateCommand_CleanupBeforeRadioOff(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const bool fPrefixPresent)
{
    return StringCchCopyA( szCmd, cchCmd, ( fPrefixPresent ) ? "+CGATT=0;" : "AT+CGATT=0;" );
}

// Create GetSimToolkitProfile command

HRESULT PDD_CreateCommand_GetSimToolkitProfile(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
    return E_NOTIMPL;
}
    
// Create SetSimToolkitProfile command

HRESULT PDD_CreateCommand_SetSimToolkitProfile(__out_ecount(cchCmd) LPSTR szCmd, size_t cchCmd, __in_ecount(cchSTKPS) LPCSTR szSTKProfileString, size_t cchSTKPS)
{
   return StringCchPrintfA( szCmd, cchCmd, "AT+STPD=%d,%s\r", cchSTKPS, szSTKProfileString );
}

// Create SimToolkitEnvelopeCmd command

HRESULT PDD_CreateCommand_SimToolkitEnvelopeCmd(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in_ecount(dwSize) const BYTE* lpbCommand, __in const DWORD dwSize)
{
    // For OEM2, we really aren't sending a Envelope command, since OEM2 does
    // not have such a command. However, the RILDrv_SendSimToolkitEnvelopeCmd
    // call is used exclusively to select a menu item from the main menu.
    // To maintain API compatibility RILDrv_SendSimToolkitEnvelopeCmd will 
    // send the OEM2 Menu Selection command +STMS.

   return StringCchPrintfA( szCmd, cchCmd, "AT+STMS=%s\r", lpbCommand );
}

// Create SIMTKCmdResponsePrefix command

HRESULT PDD_CreateCommand_SIMTKCmdResponsePrefix(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwResponseType)
{
    return StringCchCopyA( szCmd, cchCmd, ( SIM_NOTIFY_SETUPMENU_REPLY == dwResponseType ) ? "AT+STMS=" : "AT+STCR=" ); 
}

// Create SIMTKEventDownloadPrefix command

HRESULT PDD_CreateCommand_SIMTKEventDownloadPrefix(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
    return StringCchCopyA( szCmd, cchCmd, "AT+STEV=" ); 
}

HRESULT PDD_CreateCommand_BuildInfoQuery(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_BatteryInfoQuery(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return E_NOTIMPL;
}

HRESULT PDD_RebootRadio()
{
    TCHAR tszComName[6];
    bool fResult = FALSE;
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ComPort"), tszComName, 6))
    {
        _tcsncpyz(tszComName, TEXT("MUX1:"), 6);
    }

    // Try to open the COM port
    HANDLE hRebootPort = CreateFile(tszComName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if (INVALID_HANDLE_VALUE != hRebootPort)
    {
        DWORD dwBytesReturned = 0;

        fResult = (DeviceIoControl(hRebootPort, IOCTL_TTPCOM_REBOOT, NULL, 0, NULL, 0,  &dwBytesReturned, NULL) != 0);
        CloseHandle(hRebootPort);
    }

    return fResult ? S_OK : E_FAIL;
}

HRESULT PDD_CreateCommand_GetPostRebootDiagnostics(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return E_NOTIMPL;
}


HRESULT
PDD_CreateCommand_GetNITZSupported(
    __out_ecount(cchCmd) LPSTR szCmd,
    __in size_t cchCmd
)
{
    return E_NOTIMPL;
}


HRESULT PDD_GetResponseObject(__out CResponse*& pNewRsp, __in const CResponse* pSrcRsp)
{
   if (NULL == pSrcRsp) {
    pNewRsp = new CResponseTTPCOM;
    }
   else {
    pNewRsp = new CResponseTTPCOM(*(static_cast<const CResponseTTPCOM*>(pSrcRsp)));    
    }

   return (pNewRsp ? S_OK : E_OUTOFMEMORY);
}
    
HRESULT PDD_Initialize(__out const RILPDDParams*& prppRILPDDParams)
{
    bool fRetVal = TRUE;

    g_hCPINNEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!g_hCPINNEvent)
    {
        fRetVal = FALSE;
        goto Error;
    }

Error:
    return fRetVal ? S_OK : E_FAIL;
}

HRESULT PDD_Deinitialize()
{
    if (g_hCPINNEvent)
    {
        (void)CloseHandle(g_hCPINNEvent);
        g_hCPINNEvent = NULL;
    }

    return S_OK;
}

HRESULT PDD_CreateCommand_ChangeLockingPassword(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwFacility, __in LPCSTR lpszOldPassword, __in LPCSTR lpszNewPassword)
{
    // Other hardware uses +CPIN and will fail if no PIN request is pending, per GSM 07.07
    return StringCchPrintfA( szCmd, cchCmd, "AT+CPIN=\"%s\",\"%s\"\r", lpszOldPassword, lpszNewPassword );
}

HRESULT PDD_SetDownlinkVolume(__in const DWORD dwDownlinkVolume)
{
    g_dwCacheDownlinkVolume = dwDownlinkVolume;
    return S_OK;    
}

HRESULT PDD_GetDownlinkVolume(__in const bool fNormalized, __out UINT& uiDownlinkVolume)
{
   // downlink volume on OEM2 is in the range 0-100, not 0-255, so nomalize here.
   uiDownlinkVolume = (fNormalized ? (g_dwCacheDownlinkVolume*100/255) : g_dwCacheDownlinkVolume);
   return S_OK;
}

HRESULT PDD_SetUplinkVolume(__in const DWORD dwUplinkVolume)
{
    g_dwCacheUplinkVolume = dwUplinkVolume;
    return S_OK;    
}

HRESULT PDD_GetUplinkVolume(__out UINT& uiUplinkVolume)
{
   uiUplinkVolume = g_dwCacheUplinkVolume;
   return S_OK;
}

BOOL CResponseTTPCOM::ParseSTC(LPCSTR& rszPointer)
{
    FUNCTION_TRACE(CResponse::ParseSTC);
        
    UINT nstCommand;
    char szCmd[MAX_PATH];
    BOOL fRet = FALSE;

    // Get the command ID.
    if (!ParseUInt(rszPointer, FALSE, nstCommand, rszPointer))
    {
        goto Error;
    }

    // 81 is end of proactive session
    if (81 == nstCommand)
    {
        // End of Proactive session.
        m_dwCode=RIL_NOTIFY_SIMTOOLKITSESSIONEND;
        m_fUnsolicited = TRUE;
    }
    // SIM toolkit commands.
    else if(nstCommand >= 0)
    {
        // Ignore command ID of 0. 
        // A command of 0 signifies that there is no SIM toolkit support on the SIM.
        if (nstCommand != 0)
        {
            // echo back command to get the data. Ignore the response and let the notification
            // parsing handle it.
            (void)_snprintfz(szCmd, MAX_PATH, "AT+STGC=%d\r", nstCommand);
            if (!QueueCmdIgnoreRsp(APIID_NONE, szCmd, CMDOPT_IGNORERSP, g_TimeoutCmdInit, NULL, NULL, 2, 0, 0))
            {
                goto Error;
            }
        }
        
        // Send an empty SIM Toolkit event notification to let others know something is happening. 
        // Upper SIM toolkit layers will just ignore this, but an appropriate code needs
        // to be sent to prevent errors reported and problems for further command processing.
        m_dwCode = RIL_NOTIFY_SIMTOOLKITEVENT;
        m_fUnsolicited = TRUE;

    }

    fRet = TRUE;

    Error:
    return fRet;
}

BOOL CResponseTTPCOM::ParseNotificationOEM
(
    LPCSTR& szPointer,
    BOOL& fExpectCRLF,
    BOOL fDataOnNotificationPort
#ifdef RIL_WATSON_REPORT    
    , BOOL& fErrorNotification
#endif
)
{
    BOOL fRetVal = TRUE;
        
    if (MatchStringAnywhere(szPointer, "+CCRASHED 0", szPointer))
    {
        m_fUnsolicited = FALSE;
        g_fSignalQualityReceived = FALSE;
        
#ifdef RIL_RADIO_RESILIENCE
        // This indicates a radio reboot
        SignalCriticalError(RILLOG_EVENT_GENERALREINIT, __LINE__, __FILE__);        
        MakeError(RIL_E_RADIOREBOOTED);
#endif // RIL_RADIO_RESILIENCE
    }
    else if (MatchStringAnywhere(szPointer, "+CCRASHED 2", szPointer))
    {
        m_fUnsolicited = FALSE;
        g_fSignalQualityReceived = FALSE;
        
#ifdef RIL_RADIO_RESILIENCE
        // This indicates a radio reboot failure
        SignalCriticalError(RILLOG_EVENT_GENERALREINIT, __LINE__, __FILE__);        
        MakeError(RIL_E_RADIOREBOOTED);
#endif // RIL_RADIO_RESILIENCE
        DEBUGMSG(ZONE_ERROR, (TEXT("RILDrv : E : Radio has crashed - Cold Boot required.\r\n")));
        ASSERT(0);
    }
    // SIM Toolkit command notification.
    else if (MatchStringBeginning(szPointer, "+STC: ", szPointer))
    {
        if(!ParseSTC(szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    // SIM toolkit Get Command Notification
    else if (MatchStringBeginning(szPointer, "+STGC: ", szPointer) )
    {
        // Parse the SIM toolkit get command response.
        if (!ParseSIMToolkitCmdOrRsp(szPointer, RIL_NOTIFY_SIMTOOLKITCMD))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    // SIM toolkit Unsolicited Data notification
    else if (MatchStringBeginning(szPointer, "+STUD: ", szPointer))
    {
        // Parse the SIM toolkit unsolicited data.
        if (!ParseSIMToolkitCmdOrRsp(szPointer, RIL_NOTIFY_SIMTOOLKITEVENT))
        {
            fRetVal = FALSE;
            goto Error;
        }
    }
    else if (MatchStringBeginning(szPointer, "+ERRMSG: ", szPointer))
    {
        // OEM2 protocol stack emits asserts prefixed with +ERRMSG, the error
        // message follows without any quotation marks, ignore this for now
        // Should be hooked into the event logger longer term
        ParseJunk(1, szPointer);
    }
    else if (MatchStringBeginning(szPointer, "+CSQN: ", szPointer))
    {        
        LPCSTR szPostfix;
        if (!FindRspPostfix(szPointer, szPostfix))
        {
            // This isn't a complete signal quality notification -- no need to parse it
            fRetVal = FALSE;
            goto Error;
        }
        if (!g_fSignalQualityReceived)
        {
            memset(&g_rsq, 0x00, sizeof(RILSIGNALQUALITY));
        }
        g_rsq.cbSize = sizeof(RILSIGNALQUALITY);
        // OEM2 provides signal strength notifications
        if (!ParseSignalQualityData(szPointer, &g_rsq, szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }
        m_fUnsolicited = TRUE;
        m_dwCode = RIL_NOTIFY_SIGNALQUALITY;
        if (!SetBlob((void*)&g_rsq, sizeof(RILSIGNALQUALITY)))
        {
            fRetVal = FALSE;
            goto Error;
        }   
        g_fSignalQualityReceived = TRUE;
    }
    else if (MatchStringBeginning(szPointer, "+CBCN: ", szPointer))
    {
        // OEM2 provides battery level notifications, need more work on this topic
        ParseJunk(1, szPointer);
    }
    // OEM2 Some notifications have similar syntax to command responses.  THe best way to tell that
    // they are notifcations is that they come in on the notification COM port.  Parse those here.
    else if(fDataOnNotificationPort && MatchStringBeginning(szPointer, "+CFUN: ", szPointer))
    {
        ParseJunk(1, szPointer);        
    }
    else if(fDataOnNotificationPort && MatchStringBeginning(szPointer, "+CPIN: ", szPointer))
    {
        if (!ParseCPIN(szPointer))
        {
            fRetVal = FALSE;
            goto Error;
        }                
        SetEvent(g_hCPINNEvent);
    }
    else if (MatchStringBeginning(szPointer, "+CGURC: ", szPointer))
    {
        UINT nCode;

        m_fUnsolicited = TRUE;
        ParseUInt(szPointer, FALSE, nCode, szPointer);
        // We retrieve the cause, but we don't use it because we don't have an associated call id. CLCC processing
        // is invoked below to sync up the call states. If the call ID was present here, we could simply send the 
        // CPI notification and not generate the CLCC.

        // Here is the breakdown of the nCodes:
        // Parse the following integer codes (call state change notifications) as follows:
        //  0  Active call cleared with held calls  
        //  1, We are attempting to make an MO  call
        //  2, MO Call has failed for some reason   
        //  3, MO call is ringing                   
        //  4, MT call is queued (Call waiting?)    
        //  5, M0 call now connected                
        //  6, M0/MT call has disconnected          
        //  7, MO/MT call hung up           
        //  8, MO call emergency only
        //  9. MO call No Answer
        // 10. MO call Busy
        // 11, MO call terminated due to network congestion - not yet implemented by OEM2
        // 12, MO call terminated due to network dropped call - not yet implemented by OEM2
        // 1, 2, 5, 6, 10, 11, and 12 are the only state changes that require action from us.

        // There is one excption to the CLCC-Only handling case: nCode == 1. If we are attempting to make a 
        // call, we should send a CPI. The reason for this is because the CLCC may be blocked from going out
        // while the call is in progress. We do have to guess at the call id. The fact is that without call id's we can't
        // handle multiple call cases properly.
        if ( 1 == nCode )
        {
            RILCALLINFO rci;
            memset ( &rci, 0, sizeof(rci));
            rci.cbSize = sizeof ( rci );

            rci.dwStatus = RIL_CPISTAT_NEW_OUTGOING;
            rci.dwParams |= RIL_PARAM_CI_CPISTATUS;
            
            rci.dwDirection = RIL_CALLDIR_OUTGOING;   // fill this in for completeness sake
            rci.dwParams |= RIL_PARAM_CI_DIRECTION;

            rci.dwID = 1;
            rci.dwParams |= RIL_PARAM_CI_ID;

            SendRilCPIMessage ( &rci );
        }
 

        SetPotentialBogusResponseFlag ();
 
        // If we are getting a CGURC command, then we know that CLCC has changed in some what that is important to us.
        // So, now we will request the call list and parse it to see what is different than the last time that we got the call list 
        // since we get the call list everytime that the radio tells us something in the call list has changed.  This will be done
        // in the ParseGetCallList function when this returns.
        // This should be the only place that the CLCC is needed to be called in actual usage, otherwise the information
        // is sent through notifications via CPI notification, which this will turn into if changed in the ParseGetCallList function.
        if (!SetupCallListEvaluation ())
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("ParseNotification : E : Error setting up Call List Evaluation\r\n")));
        }
        // CSS_TODO:  Figure out a more elegant way of getting through this than this setup as invalid thingy.
        goto Error;     // This prevents anything from trying to be sent anywhere.
    }
    // OEM2 sends up CPINN notifications of the same form.
    else if (MatchStringBeginning(szPointer, "+CPINN: ", szPointer))
    {
        if (!ParseCPIN(szPointer))
        {
            goto Error;
        }
            m_fUnsolicited = TRUE;
        // In the CPINN case, we don't want to send up a notification of RIL_RESULT_OK
        // or RIL_RESULT_ERROR. This will just upset the proxy.
        m_fUnrecognized = TRUE;
        SetEvent(g_hCPINNEvent);
    }
    else
    {
       // Treat the "no match" case as an error
       fRetVal = FALSE;
       goto Error;
    }

Error:
    return fRetVal;
}

BOOL CResponseTTPCOM::ParseSIMToolkitCmdOrRspOEM
(
   LPCSTR& rszPointer, // IN, OUT
   const DWORD dwNotifyCode, // IN
   RILSIMTOOLKITCMD*& pCmd, // OUT
   DWORD& dwCmdSize // OUT
)
{
    BYTE* pbData = NULL;
    UINT cbData = 0;
    CRilSimToolkitCommand rstcCmd;
    DWORD dwSIMResponse=SIM_RESPONSE_OK;

    BOOL fRet = FALSE;
    LPCSTR pszEnd = NULL;

    if (RIL_NOTIFY_SIMTOOLKITEVENT == dwNotifyCode)
    {
        // Look for a "<postfix>"
        if (!FindRspPostfix(rszPointer, pszEnd))
        {
            // This isn't a complete SIM Toolkit notification -- no need to parse it
            goto Error;
        }
    }
    else
    {
        // Pass entire response to the sim toolkit layer to parse.
        // Because of the multi-line SetupMenu(ID25)/SelectItem(ID24) command, we cannot 
        // just check for the response postfix to appear anywhere.
        // It is assumed the result is always ok or error at end for now, but this should be improved.
        // 
        // Also, we need to move the end pointer before the OK/Error response.
        // This insures the result code is left at the end of the string so further
        // parsing picks up the result code and exits from the notification cleanly.
        if (MatchStringEnd(rszPointer, "\r\n0\r", pszEnd) ||
            MatchStringEnd(rszPointer, "\r\n4\r", pszEnd))
        {
            pszEnd -= strlen("0\r");
        }
        else if (MatchStringEnd(rszPointer, "\r\nOK\r", pszEnd))
        {
            pszEnd -= strlen("OK\r");
        }
        else if (MatchStringEnd(rszPointer, "\r\nERROR\r", pszEnd))
        {
            pszEnd -= strlen("ERROR\r");
        }
        else
        {
            // This isn't a complete SIM Toolkit notification -- no need to parse it
            goto Error;
        }
    }

    // Copy the data to a temporary blob so we can hold on to the data and place it into
    // cached toolkit commands if the shell isn't ready. 
    if ( (pszEnd > rszPointer) && ((pszEnd - rszPointer) >= (INT)strlen("\r\n")) )
    {
        cbData = pszEnd - rszPointer + 1;
        pbData = (BYTE*)AllocBlob(cbData);
    }
    if ( NULL == pbData )
    {
        goto Error;
    }
    memcpy((void*)pbData, rszPointer, cbData-1);

    // Null terminate the memory buffer
    pbData[cbData-1] = '\0';

    // Advance the parse pointer to the end of the data, but before
    // postfix of the AT command for correct further parsing.
    rszPointer = pszEnd - strlen("\r\n");

    pCmd = NULL;
    dwCmdSize = 0;
    dwSIMResponse = SIM_RESPONSE_OK;
    dwSIMResponse = rstcCmd.ParseCommand((LPSTR)pbData, cbData, &pCmd, &dwCmdSize);

    if (SIM_RESPONSE_OK != dwSIMResponse)
    {
        // Error parsing the command. 
        // There was a critical error if an error notification was not created.
        if (NULL == pCmd || (0 >= dwCmdSize))
        {
            goto Error;
        }

//RILSIMTOOLKITCMD* pTmpCmd = pCmd;

        // Send a response to the radio
        if (FAILED(SendSIMToolkitErrorRsp(pCmd)))
        {
            DEBUGCHK(FALSE);
        }
    }
    
    if ( !SetBlob((void*)pCmd, dwCmdSize) )
    {
        goto Error;
    }

    // Always send cmd notification.
    m_dwCode = RIL_NOTIFY_SIMTOOLKITCMD;
    m_fUnsolicited = TRUE;
    fRet = TRUE;

Error:    
    
    FreeBlob(pbData);    
    return fRet;
}

void CResponseTTPCOM::Post_ParseCPINOEM(char* szState)
{
    // On TTPCOM, we process only these 5 CPINN commands and throw away the other CPINN commands.
    if (!strcmp(szState, "SIM PIN2"))
    {
        MakeError(RIL_E_SIMPIN2REQUIRED);
    }
    else if (!strcmp(szState, "SIM PUK2"))
    {
        MakeError(RIL_E_SIMPUK2REQUIRED);
    }
    else if (!strcmp(szState, "SIM PIN"))
    {
        MakeError(RIL_E_SIMPINREQUIRED);
    }
    else if (!strcmp(szState, "SIM PUK"))
    {
        MakeError(RIL_E_SIMPUKREQUIRED);
    }
    else if (!strcmp(szState, "READY"))
    {
        m_dwCode = RIL_RESULT_OK;

        HRESULT hr = S_OK;
        if (!SetBlob((void*)&hr, sizeof(hr)))
        {
            // Error condition
        }
    }
    else
    {
        m_fUnrecognized = TRUE;
    }
}

HRESULT PDD_CreateCommand_GetATR(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd)
{
   return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_TerminateSimToolkitSession(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const DWORD dwCause)
{
   return E_NOTIMPL;
}

HRESULT PDD_CreateCommand_EnableSignalQualityNotification(__out_ecount(cchCmd) LPSTR szCmd, __in const size_t cchCmd, __in const bool fEnable)
{
   return E_NOTIMPL;
}

// NDIS stuff

DWORD WINAPI NdisNotifyThreadProc(LPVOID lpParameter);
#define MAX_NDIS_EVENT_NAME 100
#define MAX_IP_ADDRESS_LENGTH 4

HINSTANCE g_hinstOEMNDIS /*= NULL*/;
CRilNDISTTPCOM* g_pCRilNdis = NULL;


#define OEM_LIBRARY TEXT("ttpmux")
#define OEM_NdisInitialize TEXT("ttpcomNdisInitialise")
#define OEM_NdisShutdown   TEXT("ttpcomNdisShutdown")
#define OEM_NdisReceiveEvent   TEXT("ttpcomNdisReceiveEvent")
#define OEM_NdisTransmitEvent  TEXT("ttpcomNdisTransmitEvent")

typedef HANDLE  (*PFN_NDIS_INITIALIZE)(DWORD);
typedef BOOL    (*PFN_NDIS_SHUTDOWN)(HANDLE);
typedef BOOL    (*PFN_NDIS_RECEIVE_EVENT)(HANDLE, OEMNDISEVENT*,BOOL*);
typedef BOOL    (*PFN_NDIS_TRANSMIT_EVENT)(HANDLE, const OEMNDISEVENT*);

PFN_NDIS_INITIALIZE      g_pfnNdisInitialize;
PFN_NDIS_SHUTDOWN        g_pfnNdisShutdown;
PFN_NDIS_RECEIVE_EVENT   g_pfnNdisReceiveEvent;
PFN_NDIS_TRANSMIT_EVENT  g_pfnNdisTransmitEvent;



HINSTANCE LoadPacketLib()
{
    if (!g_hinstOEMNDIS)
    {
        g_hinstOEMNDIS = LoadLibrary(OEM_LIBRARY);
        ASSERT(g_hinstOEMNDIS);
    }
    return g_hinstOEMNDIS;
}

HRESULT GetPacketInterface()
{
    HRESULT hr = E_FAIL;

    HINSTANCE hinst;
    hinst = LoadPacketLib();
    if (!hinst)
    {
        ASSERT(0);
        goto Error;
    }

    g_pfnNdisInitialize =    (PFN_NDIS_INITIALIZE)GetProcAddress(hinst, OEM_NdisInitialize );
    g_pfnNdisShutdown =      (PFN_NDIS_SHUTDOWN)GetProcAddress(hinst, OEM_NdisShutdown );
    g_pfnNdisReceiveEvent =  (PFN_NDIS_RECEIVE_EVENT)GetProcAddress(hinst, OEM_NdisReceiveEvent);
    g_pfnNdisTransmitEvent = (PFN_NDIS_TRANSMIT_EVENT)GetProcAddress(hinst, OEM_NdisTransmitEvent);
    if (!g_pfnNdisInitialize || !g_pfnNdisShutdown || !g_pfnNdisReceiveEvent || !g_pfnNdisTransmitEvent)
    {
        ASSERT(0);
        goto Error;
    }
    else
    {
        hr = S_OK;
    }

Error:
    return hr;
}

BOOL ValidChannelIndex ( DWORD dwIndex )
{
    return ((dwIndex >= FIRST_VALID_NDIS_INDEX) && (dwIndex <= MAX_OEMNDIS_CHANNELS)) ? TRUE : FALSE;
    
}

// **************************************************************************
// Function Name:  myinet_addr
// 
// Purpose: Convert string IP address into a DWORD
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************

DWORD myinet_addr(const UCHAR *lpString, UCHAR length)
{
  DWORD dwIpAddr = 0;
  
  if ( length > MAX_IP_ADDRESS_LENGTH  )
    length = MAX_IP_ADDRESS_LENGTH;
     
  for(int i=0; i<length; i++)
  {
    if ( i )
      dwIpAddr <<= 8;
    dwIpAddr |= lpString[i];

  }
    
  return dwIpAddr;
}

// **************************************************************************
// Function Name:  myinet_ipv6addr
//
// Purpose: Convert string IPV6 address into a RILNDISIPV6ADDR
//
// Arguments:
//
// Return Values: RILNDISIPV6ADDR struct
//
// Side effects:
//
// Description:
// **************************************************************************
RILNDISIPV6ADDR myinet_ipv6addr(const UCHAR *lpString, UCHAR length)
{
    RILNDISIPV6ADDR ipv6addr;

    memset( &ipv6addr, 0, sizeof(ipv6addr) );

    if ( length == IPV6_ADDRESS_LENGTH )
    {
        memcpy( ipv6addr.Byte, lpString, (IPV6_ADDRESS_LENGTH_IN_UCHAR*sizeof(UCHAR)) );
    }

    return ipv6addr;
}

// **************************************************************************
// Function Name:  NdisNotifyThreadProc
// 
// Purpose:        Process NDIS events from radio.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD WINAPI NdisNotifyThreadProc(LPVOID lpParameter)
{
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;
    DWORD dwResult;

    if (!CeSetThreadPriority(GetCurrentThread(), 116))
    {
        return 0;
    }

    CRilNDISTTPCOM* pCRilNdis = static_cast<CRilNDISTTPCOM*>(pRilDevice->m_pCRilNdis);
    dwResult = pCRilNdis->NdisNotifyThread();

    return dwResult;
}


#if defined DEBUG
BOOL NdisPacketTest ( RILNDISPACKET* lpNdisPacket )
{
    BOOL fReturn = FALSE;

    DEBUGMSG(ZONE_NDISPKTS, (TEXT("RILNDIS: NdisPacketTest Packet = %x \r\n"), lpNdisPacket));    

    if ( lpNdisPacket )
    {
        if ( lpNdisPacket->cBufferCount )
        {
            DEBUGMSG(ZONE_NDISPKTS, (TEXT("RILNDIS: NdisPacketTest cBufferCount = %d \r\n"), lpNdisPacket->cBufferCount));
            LPRILNDISBUFFER lpRilNdisBuffer = lpNdisPacket->NDISBuffer;
            DWORD dwBufferCount=0;
            for(DWORD i=0; i<lpNdisPacket->cBufferCount;i++)
            {
                DEBUGMSG(ZONE_NDISPKTS, (TEXT("RILNDIS: NdisPacketTest NDISBuffer[%d] = %x \r\n"), i, lpRilNdisBuffer));
                if ( lpRilNdisBuffer )
                {
                    DEBUGMSG(ZONE_NDISPKTS, (TEXT("RILNDIS: NdisPacketTest NDISBuffer[%d].pbyBytes = %x, cByteCount = %x \r\n"), i, lpRilNdisBuffer->pbyBytes,lpRilNdisBuffer->cByteCount));
                    if (lpRilNdisBuffer->pbyBytes /*&& lpRilNdisBuffer->cByteCount*/)
                    {
                        /* Apparently we can get buffers without any data. Don't fail the packet test if this is true */
                        dwBufferCount++;
                        lpRilNdisBuffer++;
                        continue;
                    }
                    break;
                }
            }
            if ( dwBufferCount == lpNdisPacket->cBufferCount )
            {
                fReturn = TRUE;
            }
        }
    }
    ASSERT(fReturn);
    return ( fReturn );
}

void DumpPacket (RILNDISPACKET* lpPacket)
{
    if (ZONE_NDISPKTS)
    {
        if ( lpPacket )
        {
            LPRILNDISBUFFER lpRilNdisBuffer = NULL;
            wchar_t wLineBuf[256];
            const size_t LineBufCch = sizeof(wLineBuf) / sizeof(*wLineBuf);
            LPWSTR pLineBuf;
            size_t LineBufRemainingCch;
                    
                           
            DEBUGMSG( ZONE_NDISPKTS , (TEXT("RILNDIS DumpPacket lpPacket = %x, dwSize = %x, dwContextId = %x, cBufferCount = %x,\r\n"), 
                             lpPacket, 
                             lpPacket->dwSize, 
                             lpPacket->dwContextId,
                             lpPacket->cBufferCount));  
                             
               
           lpRilNdisBuffer = lpPacket->NDISBuffer;

           for(DWORD i=0; i<lpPacket->cBufferCount; i++)
           {
                DEBUGMSG( ZONE_NDISPKTS, (TEXT("RILNDIS: Dump Packet Buffer = %0x cByteCount = %0x"), lpRilNdisBuffer,  lpRilNdisBuffer->cByteCount));
                
                pLineBuf = wLineBuf;
                LineBufRemainingCch = LineBufCch;
                for(DWORD x=0; x<lpRilNdisBuffer->cByteCount; x++)
                {
                    StringCchPrintfExW( pLineBuf, LineBufRemainingCch, &pLineBuf, &LineBufRemainingCch, STRSAFE_IGNORE_NULLS,
                                        L"%.2X ", lpRilNdisBuffer->pbyBytes[ x ] );

                    if ( (pLineBuf - wLineBuf) > 45 )
                    {
                        DEBUGMSG( ZONE_NDISPKTS,(wLineBuf));
                        pLineBuf = wLineBuf;
                        LineBufRemainingCch = LineBufCch;
                    }
                }
                if ( pLineBuf > wLineBuf )
                {
                    DEBUGMSG( ZONE_NDISPKTS,(wLineBuf));
                }
                lpRilNdisBuffer++; 
           }
       }
    }
}

void DumpNdisIPConfig( RILNDISIPCONFIG* pRilNdisIpConfig )
{
    if ( pRilNdisIpConfig )
        {
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig cbSize = %x \r\n"),pRilNdisIpConfig->cbSize));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig dwContextId = %x \r\n"),pRilNdisIpConfig->dwContextId));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig dwProtocol = %x \r\n"),pRilNdisIpConfig->dwProtocol));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.dwFlags = %x \r\n"),pRilNdisIpConfig->ipv4.dwFlags));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inIPAddress = %x \r\n"),pRilNdisIpConfig->ipv4.inIPAddress));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inPrimaryDNS = %x \r\n"),pRilNdisIpConfig->ipv4.inPrimaryDNS));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inSecondaryDNS = %x \r\n"),pRilNdisIpConfig->ipv4.inSecondaryDNS));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inDefaultGateway = %x \r\n"),pRilNdisIpConfig->ipv4.inDefaultGateway));
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inSubnetMask = %x \r\n"),pRilNdisIpConfig->ipv4.inSubnetMask));

        }
}

void DEBUG_TYPE( OEMNDISEVENTTYPE type)
{
    switch ( type )
    {
        case OEM_NDIS_NULL_EVENT:            /* no event available                          */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_NULL_EVENT\r\n")));
            break;
        case OEM_NDIS_RX_PACKET:             /* Received a packet                           */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_RX_PACKET\r\n")));
            break;
        case OEM_NDIS_TX_PACKET_COMPLETE:    /* TX packet done - buffer can be reused       */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_TX_PACKET_COMPLETE\r\n")));
            break;
        case OEM_NDIS_IP_CONFIGURATION:      /* NDIS configuration from network             */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_IP_CONFIGURATION\r\n")));
            break;
        case OEM_NDIS_XOFF:                  /* Please stop sending data to me              */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_XOFF\r\n")));
            break;
        case OEM_NDIS_XON:                   /* Restart sending data to me                  */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_XON\r\n")));
            break;
        case OEM_NDIS_INITIALISE_COMPLETE:   /*Initialise is now complete                   */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_INITIALISE_COMPLETE\r\n")));
            break;
        case OEM_NDIS_OPEN_COMPLETE:         /* Open is now complete                        */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_OPEN_COMPLETE\r\n")));
            break;
        case OEM_NDIS_CLOSE_COMPLETE:        /* Close is now complete                       */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_CLOSE_COMPLETE\r\n")));
            break;
        case OEM_NDIS_TX_PACKET:             /* Request to transmit a packet                */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_TX_PACKET\r\n")));
            break;
        case OEM_NDIS_RX_PACKET_COMPLETE:    /* RX packet complete - buffer can be reused   */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_RX_PACKET_COMPLETE\r\n")));
            break;
        case OEM_NDIS_OPEN_CONNECTION:       /* open the connection                         */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_OPEN_CONNECTION\r\n")));
            break;
        case OEM_NDIS_CLOSE_CONNECTION:      /* close the connection                        */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_CLOSE_CONNECTION\r\n")));
            break;
        case OEM_NDIS_REGISTRATION_STATUS:   /* gsm or gprs registration status change      */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_REGISTRATION_STATUS\r\n")));
            break;
        case OEM_NDIS_BUFFER_STATUS:         /* buffer state has changed or response       */
            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  OEM_NDIS_BUFFER_STATUS\r\n")));
            break;
    }
}

void PacketPointers (RILNDISPACKET* lpPacket)
{
    if ( lpPacket )
    {
        LPRILNDISBUFFER pRilNdisBufferIn = NULL;
        
        DEBUGMSG( ZONE_NDISPKTS, (TEXT("RILNDIS: PacketPointer  Packet = %0x, Count = %x"), lpPacket, lpPacket->cBufferCount));
        
        if ( lpPacket->cBufferCount )
        {
            pRilNdisBufferIn = lpPacket->NDISBuffer;
                
            for(DWORD i=0; i<lpPacket->cBufferCount; i++)
            {
                DEBUGMSG( ZONE_NDISPKTS, (TEXT("RILNDIS: PacketPointer Buffer = %0x "), pRilNdisBufferIn->pbyBytes));
                pRilNdisBufferIn++;
            }
        }
    }
}
#else
#define PacketPointers(a)
#define DEBUG_TYPE(a)
#define DumpPacket(a)
#define NdisPacketTest(a)
#define DumpNdisIPConfig(a)
#endif

CRilNDISTTPCOM::CRilNDISTTPCOM()
{
    m_pCrilHandle = NULL;
    m_fXon = RIL_NDIS_XON;
    m_hNdisNotifyThread = NULL;
    g_pCRilNdis = this;
    m_hCloseAllConnections = NULL;
}

CRilNDISTTPCOM::~CRilNDISTTPCOM()
{
    m_pCrilHandle = NULL;
    g_pCRilNdis = NULL;
}

void CRilNDISTTPCOM::Init(PVOID pHandle)
{
    m_pCrilHandle = pHandle;
}

// **************************************************************************
// Function Name: NdisStart
// 
// Purpose:       Initialize to a known state and start NDIS thread.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDISTTPCOM::NdisStart ( HANDLE hCancelEvent )
{
    m_hNdisNotifyThread = NULL;
    m_dwNdisNotifyThreadID = 0;
    m_hCancelEvent = hCancelEvent;
    memset( m_rgpNdisChannels, 0, sizeof(m_rgpNdisChannels)); 
    m_hNdisNotifyThread = CreateThread(NULL, 0, NdisNotifyThreadProc, (LPVOID)m_pCrilHandle, 0, &m_dwNdisNotifyThreadID);
    if ( m_hNdisNotifyThread )
    {
        InitializeCriticalSection(&m_csNdisEventList);
        InitializeCriticalSection(&m_csConnectionList);
        InitializeCriticalSection(&m_PdpContextCriticalSection);
        InitializeListHead(&m_PdpContextPendingList);
        InitializeListHead(&m_PdpContextFreeList);

    }

    if ( FAILED(GetPacketInterface()))
    {
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisStart GetPacketInterface FAILED \r\n")));
        ASSERT(0);
    }

    return (m_hNdisNotifyThread ? TRUE : FALSE );
}

// **************************************************************************
// Function Name: NdisStop
// 
// Purpose:       Bring down to a known state. Wait for NDIS thread to terminate.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDISTTPCOM::NdisStop( void )
{
    if ( m_hNdisNotifyThread )
    {
      (void)WaitForSingleObject(m_hNdisNotifyThread,10000);
      (void)CloseHandle(m_hNdisNotifyThread);
      m_hNdisNotifyThread = NULL;
      m_dwNdisNotifyThreadID = 0;
      DeleteCriticalSection(&m_csNdisEventList);
      DeleteCriticalSection(&m_csConnectionList);
      DeleteCriticalSection(&m_PdpContextCriticalSection);

    }
}

// **************************************************************************
// Function Name:   NdisCountEvents
// 
// Purpose:         Convenience routine. Count number of events in WaitForMultipleObjects list.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD CRilNDISTTPCOM::NdisCountEvents(void)
{
    DWORD dwCount=0;
    
    for(int i=0;i<MAX_OEMNDIS_EVENTS;i++)
    {
       if ( !m_rgpNdisEvents.lpNdisEventHandlesPrimary[i] )
         break;
       else
         dwCount++;
    }

    return dwCount;
}

// **************************************************************************
// Function Name:  NdisGetFreeNdisEntry
// 
// Purpose:        Find an unused NDIS channel.
//
// Arguments:      OUT index value if one is available.
//
// Return Values:  E_FAIL if all indeces are in use.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT    CRilNDISTTPCOM::NdisGetFreeNdisEntry ( DWORD *lpIndex )
{
    HRESULT hr = E_FAIL;
   
    EnterCriticalSection(&m_csConnectionList); 
    for(DWORD i = FIRST_VALID_NDIS_INDEX; i < MAX_OEMNDIS_CHANNELS; i++)
    {
      if ( 0 == m_rgpNdisChannels[i].hNdisHandle )
      {
         hr = S_OK;
         *lpIndex = i;
         break;
      }
    }
    LeaveCriticalSection(&m_csConnectionList);
    return hr;
}

// **************************************************************************
// Function Name:   NdisRemoveNdisEntry
// 
// Purpose:         Free an NDIS channel for use by others.
//
// Arguments:       IN Handle to the NDIS channel
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void    CRilNDISTTPCOM::NdisRemoveNdisEntry ( HANDLE hNdisHandle )
{
    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
    {
      if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) ) 
      {
        m_NdisAsyncCommandList.RemoveAllCommands( hNdisHandle );
        CloseHandle(hNdisHandle);
        memset( &m_rgpNdisChannels[i], 0, sizeof(OEMNDISCHANNELS)); 
        break;
      }
    }
    LeaveCriticalSection(&m_csConnectionList);            
}

// **************************************************************************
// Function Name: NdisCidFromHandle
// 
// Purpose:       Find the cid associated to a given NDIS channel handle.
//
// Arguments:     IN NdisHandle, OUT lpCid
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilNDISTTPCOM:: NdisCidFromHandle( HANDLE hNdisHandle, DWORD *lpCid )
{
    HRESULT hr = E_FAIL;
    EnterCriticalSection(&m_csConnectionList); 
    for(int i=FIRST_VALID_NDIS_INDEX; i < MAX_OEMNDIS_CHANNELS; i++)
      if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) )
      {
         *lpCid = m_rgpNdisChannels[i].dwCid;
         hr = S_OK;
         break;
      }

    DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisCidFromHandle hr = %s ContextID = %x \r\n"), 
              (hr == S_OK) ? L"S_OK" : L"E_FAIL", *lpCid));
    LeaveCriticalSection(&m_csConnectionList);    
    return hr;
}

// **************************************************************************
// Function Name: NdisOemNdisOpenRequestFromHandle
// 
// Purpose:       Find the cid associated to a given NDIS channel handle.
//
// Arguments:     IN NdisHandle, OUT lpCid
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilNDISTTPCOM:: NdisOemNdisOpenRequestFromHandle( HANDLE hNdisHandle, RILNDISGPRSCONTEXT *lpRilNdisGprsContext )
{
    HRESULT hr = E_FAIL;
    EnterCriticalSection(&m_csConnectionList);    
    for(int i=FIRST_VALID_NDIS_INDEX; i < MAX_OEMNDIS_CHANNELS; i++)
    {
      if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) && lpRilNdisGprsContext)
      {
         memmove( lpRilNdisGprsContext, &(m_rgpNdisChannels[i].RilNdisGprsContext), sizeof(*lpRilNdisGprsContext));
         hr = S_OK;
         break;
      }
    }

    DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisOemNdisOpenRequestFromHandle hr = %s  \r\n"), 
              (hr == S_OK) ? L"S_OK" : L"E_FAIL"));
    LeaveCriticalSection(&m_csConnectionList);    
    return hr;
}

// **************************************************************************
// Function Name: NdisHandleFromCid
// 
// Purpose:       Search of the NDIS channel handle associated with a cid.
//
// Arguments:     IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT CRilNDISTTPCOM::NdisHandleFromCid( DWORD cid, HANDLE *hNdisHandle )
{
    HRESULT hr = E_FAIL;
    EnterCriticalSection(&m_csConnectionList);    
    if ( TRUE == ValidChannelIndex ( cid ) )
    {
        for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
            if ( (m_rgpNdisChannels[i].dwCid == cid) && m_rgpNdisChannels[i].hNdisHandle )
            {
                *hNdisHandle = m_rgpNdisChannels[i].hNdisHandle;
                hr = S_OK;
                break;
            }
        }
    }
    LeaveCriticalSection(&m_csConnectionList);    
    return hr;
}

// **************************************************************************
// Function Name: NdisRilCallbackFromCID
// 
// Purpose:       Search of the NDIS channel handle associated with a cid.
//
// Arguments:     IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDISTTPCOM::NdisRilCallbacksFromCID( DWORD ContextID, PVOID *pCallbackContext, RILNDISRECEIVECALLBACK *pfnRilReceiveCallback, RILNDISTRANSMITCALLBACK *pfnRilTransmitCallback, RILNDISSTATUSCALLBACK *pfnRilStatusCallback )
{
    EnterCriticalSection(&m_csConnectionList);
    *pfnRilReceiveCallback = NULL;
    *pfnRilTransmitCallback = NULL;
    *pfnRilStatusCallback = NULL;
    *pCallbackContext = NULL;
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
        if ( ContextID && (m_rgpNdisChannels[i].dwCid == ContextID) )
            {
            *pfnRilReceiveCallback = m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisReceive;
            *pfnRilTransmitCallback = m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisTransmit;
            *pfnRilStatusCallback = m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisStatus;
            *pCallbackContext = m_rgpNdisChannels[i].RilNdisGprsContext.pCallbackContext;
            break;
            }
        }
    LeaveCriticalSection(&m_csConnectionList);   
}

// **************************************************************************
// Function Name: NdisRilInstanceFromHandle
// 
// Purpose:       Search of the NDIS channel handle associated with a cid.
//
// Arguments:     IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
VOID* CRilNDISTTPCOM::NdisRilInstanceFromHandle( HANDLE hNdisHandle )
{
    HRESULT hr = E_FAIL;
    VOID *pVoid = NULL;
    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
    {
        if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) )
        {
            pVoid = m_rgpNdisChannels[i].dwCrilInstance;
            hr = S_OK;
            break;
        }
    }
    LeaveCriticalSection(&m_csConnectionList);   
    return pVoid;
}

// **************************************************************************
// Function Name: NdisRemoveCallbacks
// 
// Purpose:       
//
// Arguments:     
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void    CRilNDISTTPCOM::NdisRemoveCallbacks ( DWORD ContextID )
{    
    DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisRemoveCallbacks cid = %x  \r\n"), ContextID));
    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
    {
        if ( m_rgpNdisChannels[i].RilNdisGprsContext.dwContextID == ContextID )
        {
            m_rgpNdisChannels[i].RilNdisGprsContext.pCallbackContext = NULL;
            m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisReceive = NULL;
            m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisStatus = NULL;
            m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisTransmit = NULL;
            break;
        }
    }
    LeaveCriticalSection(&m_csConnectionList);
}


// **************************************************************************
// Function Name: NdisAddNdisEntry
// 
// Purpose:       Add an NDIS channel entry to mark its use.
//
// Arguments:     IN cid, dwIndex, hHandle (NDIS channel handle), lpOemNdisEvent CrilInstance open structure
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void    CRilNDISTTPCOM::NdisAddNdisEntry ( DWORD cid, DWORD dwIndex,  HANDLE hHandle, VOID *hCrilInstance, const RILNDISGPRSCONTEXT *lpRilNdisGprsContext )
{    
    DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisAddNdisEntry cid = %x, index = %x, handle = %x  \r\n"), cid, dwIndex, hHandle));
    EnterCriticalSection(&m_csConnectionList);
    if ( hHandle && (TRUE == ValidChannelIndex ( dwIndex )) )
    {
        m_rgpNdisChannels[dwIndex].dwCid= cid;
        m_rgpNdisChannels[dwIndex].hNdisHandle = hHandle;
        m_rgpNdisChannels[dwIndex].dwCrilInstance = hCrilInstance;
        CeSafeCopyMemory(&m_rgpNdisChannels[dwIndex].RilNdisGprsContext, lpRilNdisGprsContext, sizeof(m_rgpNdisChannels[dwIndex].RilNdisGprsContext));
    }
    LeaveCriticalSection(&m_csConnectionList);
}

// **************************************************************************
// Function Name:  NdisConvertConfig
// 
// Purpose: 
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDISTTPCOM::NdisConvertConfig ( const OEMNDISCONFIGURATION* lpOemNdisConfig,  RILNDISIPCONFIG* lpRilNdisIpConfig)
{
    if ( lpOemNdisConfig && lpRilNdisIpConfig )
    {
        memset( lpRilNdisIpConfig, 0, sizeof(RILNDISIPCONFIG));
        lpRilNdisIpConfig->cbSize = sizeof(RILNDISIPCONFIG);

        /* derive whether address is IPv4(default) or IPv6 */
        lpRilNdisIpConfig->dwProtocol = RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV4;
        if ( lpOemNdisConfig->ipAddress.length == IPV6_ADDRESS_LENGTH )
        {
            lpRilNdisIpConfig->dwProtocol = RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV6;
        }

        /* fill in protocol specific parameters */
        if ( lpRilNdisIpConfig->dwProtocol == RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV4 )
        {
            lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_SUBNETMASK;
            lpRilNdisIpConfig->ipv4.inSubnetMask = 0xffffff00;

            if ( lpOemNdisConfig->ipAddress.length && lpOemNdisConfig->ipAddress.address )
            {
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPADDR;
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_DEFAULTGATEWAY;
                lpRilNdisIpConfig->ipv4.inIPAddress = myinet_addr( lpOemNdisConfig->ipAddress.address, lpOemNdisConfig->ipAddress.length );
                lpRilNdisIpConfig->ipv4.inDefaultGateway = lpRilNdisIpConfig->ipv4.inIPAddress ^ 0xFF;
            }

            if ( lpOemNdisConfig->primaryDnsAddress.length && lpOemNdisConfig->primaryDnsAddress.address)
            {
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_PRIMARYDNS;
                lpRilNdisIpConfig->ipv4.inPrimaryDNS = myinet_addr( lpOemNdisConfig->primaryDnsAddress.address, lpOemNdisConfig->primaryDnsAddress.length );
            }

            if ( lpOemNdisConfig->secondaryDnsAddress.length && lpOemNdisConfig->secondaryDnsAddress.address)
            {
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_SECONDARYDNS;
                lpRilNdisIpConfig->ipv4.inSecondaryDNS = myinet_addr( lpOemNdisConfig->secondaryDnsAddress.address, lpOemNdisConfig->secondaryDnsAddress.length );
            }
        }
        else if ( lpRilNdisIpConfig->dwProtocol == RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV6 )
        {
            if ( lpOemNdisConfig->ipAddress.length )
            {
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_IPADDR;
                lpRilNdisIpConfig->ipv6.inIPAddress = myinet_ipv6addr( lpOemNdisConfig->ipAddress.address, lpOemNdisConfig->ipAddress.length );

                UCHAR subnetmask[IPV6_ADDRESS_LENGTH];
                memset( subnetmask, 0xFF, sizeof(subnetmask) );
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_SUBNETMASK;
                lpRilNdisIpConfig->ipv6.inSubnetMask = myinet_ipv6addr( subnetmask, IPV6_ADDRESS_LENGTH );

                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_DEFAULTGATEWAY;
                lpRilNdisIpConfig->ipv6.inDefaultGateway = lpRilNdisIpConfig->ipv6.inIPAddress;
            }

            if ( lpOemNdisConfig->primaryDnsAddress.length )
            {
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_PRIMARYDNS;
                lpRilNdisIpConfig->ipv6.inPrimaryDNS = myinet_ipv6addr( lpOemNdisConfig->primaryDnsAddress.address, lpOemNdisConfig->primaryDnsAddress.length );
            }

            if ( lpOemNdisConfig->secondaryDnsAddress.length )
            {
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_SECONDARYDNS;
                lpRilNdisIpConfig->ipv6.inSecondaryDNS = myinet_ipv6addr( lpOemNdisConfig->secondaryDnsAddress.address, lpOemNdisConfig->secondaryDnsAddress.length );
            }
        }
    }
}

// **************************************************************************
// Function Name:  NdisCloseAllConnections
// 
// Purpose:        Close all NDIS connections
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void   CRilNDISTTPCOM::NdisCloseAllConnections ( void )
{
    OEMNDISEVENT sEvent;
    DWORD ContextID = 0;

    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
        if ( m_rgpNdisChannels[i].hNdisHandle  )
            {
            if ( S_OK  == NdisCidFromHandle( m_rgpNdisChannels[i].hNdisHandle, &ContextID) )
                {
                RemoveAndSignalAllPdpContextEntries(ContextID);
                }
            if ( ContextID )
               {
                sEvent.type = OEM_NDIS_CLOSE_CONNECTION;
                sEvent.event.open.cid = ContextID;
                NdisInterfaceTransmitEvent( m_rgpNdisChannels[i].hNdisHandle , &sEvent);
                if ( NULL == m_hCloseAllConnections )
                    {
                    m_hCloseAllConnections = CreateEvent (NULL, FALSE, FALSE, NULL );
                    }
                }
            }
        }
    LeaveCriticalSection(&m_csConnectionList);

    if ( m_hCloseAllConnections )
        {
        WaitForSingleObject( m_hCloseAllConnections, 60L * 1000L );
        CloseHandle(m_hCloseAllConnections);
        m_hCloseAllConnections = NULL;
        }
}

// **************************************************************************
// Function Name:   NdisShutdownConnection
// 
// Purpose:         Shutdown and NDIS connection and cleanup related items.
//
// Arguments:       IN handle to channel to close.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDISTTPCOM:: NdisShutdownConnection ( HANDLE hHandle )
{

    if ( hHandle )
    {
        // We want to Shutdown this handle and remove from NDIS channel information.
        NdisInterfaceShutdown(hHandle);         
        // Notify it's down
        RILGPRSCONTEXTACTIVATED RilGprsContexActivated;
        RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
        RilGprsContexActivated.fActivated = FALSE;
        RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEDEACT;
        if ( S_OK  == NdisCidFromHandle( hHandle, &RilGprsContexActivated.dwContextID) )
            ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
        // Remove the entry from our list of Ndis handles/Cids
        NdisRemoveNdisEntry(hHandle);
        // Now remove the event object.
        NdisRemoveNotifyEvent(hHandle);
    }

    // See if there are any other connections. If not, set the event for the CloseAllConnections call.
    BOOL fEmpty = TRUE;
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
        if ( m_rgpNdisChannels[i].hNdisHandle  )
            {
            fEmpty = FALSE;
            break;
            }
        }

    if ( (TRUE == fEmpty) && m_hCloseAllConnections )
        {
        SetEvent ( m_hCloseAllConnections );
        }

}

void RilNdisSendPacket (const LPRILNDISPACKET lpPacketToSend)
{
    if ( g_pCRilNdis && lpPacketToSend)
    {
       {
           HANDLE hNdis;
           if ( S_OK == g_pCRilNdis->NdisHandleFromCid( lpPacketToSend->dwContextId, &hNdis ))
           {
               CRilInstanceNDISTTPCOM *pCRilInstanceNdis;
               pCRilInstanceNdis = (CRilInstanceNDISTTPCOM*)g_pCRilNdis->NdisRilInstanceFromHandle( hNdis );
               if ( pCRilInstanceNdis )
               {
               pCRilInstanceNdis->NdisSendPacket(lpPacketToSend);
               }
           }
       }          
    }
}

void RilNdisRecievePacketDone(const LPRILNDISPACKET lpPacketReceived)
{
    if ( g_pCRilNdis && lpPacketReceived)
    {
       {
           HANDLE hNdis;
           if ( S_OK == g_pCRilNdis->NdisHandleFromCid( lpPacketReceived->dwContextId, &hNdis ))
           {
               CRilInstanceNDISTTPCOM *pCRilInstanceNdis;
               pCRilInstanceNdis = (CRilInstanceNDISTTPCOM*)g_pCRilNdis->NdisRilInstanceFromHandle( hNdis );
               if ( pCRilInstanceNdis )
               {
                   pCRilInstanceNdis->NdisReceivePacketDone(lpPacketReceived);
               }
           }
       }          
    }    
}

// **************************************************************************
// Function Name: NdisNotifyThread
// 
// Purpose:       Thread running to process NDIS events.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD   CRilNDISTTPCOM:: NdisNotifyThread(void)
{
    DWORD dwTriggered, dwIndex, ChannelIndex=0;
    BOOL fMoreEvents,fSuccess;
    OEMNDISAPIREFERENCE OemNdisApiReference;
    HRESULT hr;
    RILNDISIPCONFIG RilNdisIpConfig;
    OEMNDISEVENT OemNdisEvent,ReceiveOemNdisEvent;
    CRilInstanceHandle *pCrilInstanceHandle = NULL;
    RILNDISGPRSCONTEXT RilNdisGprsContext;
    RILGPRSCONTEXTACTIVATED RilGprsContexActivated;
    DWORD               ResultDetails = 0;
    DWORD               ContextID = 0;
    HANDLE              hNdisChannelHandle = NULL;
    BOOL                HandlePdpContextListEntry = FALSE;
    BOOL                ProcessCommandFlag = FALSE;
    RILNDISTRANSMITCALLBACK pfnRilTransmitCallback;
    RILNDISSTATUSCALLBACK   pfnRilStatusCallback;
    RILNDISRECEIVECALLBACK pfnRilReceiveCallback;
    PVOID                  pCallbackContext;
    RILNDISSTATUS       RilNdisStatus;
    DWORD               ContextIDIndex;

    // Tell the main thread that we've reached the checkpoint
    ((CRilHandle*)m_pCrilHandle)->GetCheckPoint()->Reached();
    
    memset ( &m_rgpNdisEvents, 0, sizeof(OEMNDISWAITEVENTOBJECT));

    // Create the event used to indicate new events
    m_rgpNdisEvents.hNdisAddChannelEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if(!m_rgpNdisEvents.hNdisAddChannelEvent)
    {
        goto Exit;
    }

    m_rgpNdisEvents.lpNdisEventHandlesPrimary[NDIS_ADD_CHANNEL_EVENT_INDEX] = m_rgpNdisEvents.hNdisAddChannelEvent ;
    m_rgpNdisEvents.lpNdisEventHandlesUpdate[NDIS_ADD_CHANNEL_EVENT_INDEX] = m_rgpNdisEvents.hNdisAddChannelEvent ;
    m_rgpNdisEvents.nObjectCount = 1;

    m_rgpNdisEvents.lpNdisEventHandlesPrimary[NDIS_STOP_THREAD_INDEX] = m_hCancelEvent;
    m_rgpNdisEvents.lpNdisEventHandlesUpdate[NDIS_STOP_THREAD_INDEX] =  m_hCancelEvent;
    m_rgpNdisEvents.nObjectCount++;
    
    do 
    {
        dwTriggered = WaitForMultipleObjects( m_rgpNdisEvents.nObjectCount, m_rgpNdisEvents.lpNdisEventHandlesPrimary, FALSE, INFINITE );
        dwIndex = dwTriggered - WAIT_OBJECT_0;

        // The events from NDIS are created with manual reset enabled. The event will be reset by the RADIO when
        // the last event is read from the queue.
        if ( dwIndex == NDIS_ADD_CHANNEL_EVENT_INDEX ) // index reserved for the m_rgpNdisEvents.hNdisAddChannelEvent  event.
        {
            EnterCriticalSection(&m_csNdisEventList);
            memmove( m_rgpNdisEvents.lpNdisEventHandlesPrimary, 
                     m_rgpNdisEvents.lpNdisEventHandlesUpdate, 
                     sizeof(m_rgpNdisEvents.lpNdisEventHandlesPrimary));
        
            m_rgpNdisEvents.nObjectCount = NdisCountEvents();
            LeaveCriticalSection(&m_csNdisEventList);
            continue; // Get the upcoming notification
        }
        
        if ( dwIndex == NDIS_STOP_THREAD_INDEX )
        {
            NdisCloseAllConnections();
            break;
        }

        // The notification is coming from the RADIO
        else 
        {
            // We maintain a parallel array that contains the RADIO handle so we don't need to extract them from the event 
            // name.  Get RADIO events and process them.
            do {
                   memset ( &ReceiveOemNdisEvent, 0, sizeof(OEMNDISEVENT));
                   fSuccess = NdisInterfaceReceiveEvent( m_rgpNdisEvents.lpNdisHandles[dwIndex],  &ReceiveOemNdisEvent, &fMoreEvents);
                   if ( fSuccess )
                   {
                       DEBUG_TYPE(ReceiveOemNdisEvent.type);
                       memset(&OemNdisApiReference,0,sizeof(OemNdisApiReference)); 
                       OemNdisApiReference.hNdisHandle = m_rgpNdisEvents.lpNdisHandles[dwIndex]; // the radio handle
               
                       switch ( ReceiveOemNdisEvent.type )
                       {
                           case OEM_NDIS_RX_PACKET: 
                               ReceiveOemNdisEvent.event.packet_p->dwContextId = 0; 
                               NdisCidFromHandle ( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ReceiveOemNdisEvent.event.packet_p->dwContextId );    
        
                               DumpPacket(ReceiveOemNdisEvent.event.packet_p);
                               PacketPointers ( ReceiveOemNdisEvent.event.packet_p );

                               pfnRilReceiveCallback = NULL;
                               NdisRilCallbacksFromCID( ReceiveOemNdisEvent.event.packet_p->dwContextId,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );

                               if ( pfnRilReceiveCallback )
                               {
                                   pfnRilReceiveCallback(pCallbackContext,ReceiveOemNdisEvent.event.packet_p);
                               }
                               else // The context must be closed
                               {
                                   DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: OEM_NDIS_RX_PACKET WARNING Context Closed - Freeing \r\n")));  
                                   ReceiveOemNdisEvent.type = OEM_NDIS_RX_PACKET_COMPLETE;
                                   NdisInterfaceTransmitEvent( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ReceiveOemNdisEvent);
                               }        
                               break;
                    
                           case OEM_NDIS_TX_PACKET_COMPLETE:
                               NdisCidFromHandle ( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ReceiveOemNdisEvent.event.packet_p->dwContextId );            
                               pfnRilTransmitCallback = NULL;
                               NdisRilCallbacksFromCID( ReceiveOemNdisEvent.event.packet_p->dwContextId,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );

                               if ( pfnRilTransmitCallback )
                               {
                                   pfnRilTransmitCallback(pCallbackContext,ReceiveOemNdisEvent.event.packet_p);
                               }
                               else // The context must be closed
                               {
                                   DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: OEM_NDIS_TX_PACKET_COMPLETE WARNING Context Closed - Freeing \r\n")));  
                               }        
                               break;
                    
                           case OEM_NDIS_IP_CONFIGURATION:     
                               memset ( &RilNdisIpConfig, 0, sizeof(RILNDISIPCONFIG));
                               NdisConvertConfig ( &ReceiveOemNdisEvent.event.configuration, &RilNdisIpConfig);
                               NdisCidFromHandle ( m_rgpNdisEvents.lpNdisHandles[dwIndex], &RilNdisIpConfig.dwContextId ); 
                               DumpNdisIPConfig( &RilNdisIpConfig );
                               OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                               OemNdisApiReference.dwStatus = 0; // No error
                               OemNdisApiReference.eEventType= OEM_NDIS_OPEN_COMPLETE;
                               OemNdisApiReference.ContextID = ContextID;
                               OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisApiReference.dwStatus;
                               ContextID = 0;
                               NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                               OemNdisApiReference.RilNdisGprsContextResponse.dwContextID = ContextID;
                               OemNdisApiReference.RilNdisGprsContextResponse.pfnNdisReceivePacketDone = RilNdisRecievePacketDone;
                               OemNdisApiReference.RilNdisGprsContextResponse.pfnNdisSendPacket = RilNdisSendPacket;
                               CeSafeCopyMemory(&OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig, &RilNdisIpConfig,sizeof(OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig));
                               OemNdisApiReference.RilNdisGprsContextResponse.dwParams = RIL_PARAM_RILNDISGPRSCONTEXTRESPONSE_IPCONFIG | RIL_PARAM_RILNDISGPRSCONTEXTRESPONSE_FUNCTIONS;
                               ProcessCommandFlag = m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference ); 
                               ProcessNextContextRequest();                       
                               break;
                    
                           case OEM_NDIS_XOFF:   
                           case OEM_NDIS_XON:   
                                RilNdisStatus.dwContextId=0;
                                RilNdisStatus.dwSize=sizeof(RilNdisStatus);
                                RilNdisStatus.dwStatusType=RIL_PARAM_RILNDISSTATUS_FLOWCONTROL;
                                RilNdisStatus.dwFlowControl = (ReceiveOemNdisEvent.type == OEM_NDIS_XON) ? RIL_NDIS_XON : RIL_NDIS_XOFF;
                                m_fXon = RilNdisStatus.dwFlowControl;
                                pfnRilStatusCallback = NULL;
                                for( ContextIDIndex=FIRST_VALID_NDIS_INDEX;ContextIDIndex<MAX_OEMNDIS_CHANNELS;ContextIDIndex++)
                                    {   
                                    NdisRilCallbacksFromCID( ContextIDIndex,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );
   
                                    if ( pfnRilStatusCallback )
                                        {
                                        pfnRilStatusCallback(pCallbackContext,&RilNdisStatus);
                                        }
                                    else 
                                        {
                                        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: %s WARNING No Callback on context %x\r\n"),(ReceiveOemNdisEvent.type == OEM_NDIS_XON) ? L"OEM_NDIS_XON" : L"OEM_NDIS_XOFF", ContextIDIndex));  
                                        }   
                                    }                      
                               break;
                    
                           case OEM_NDIS_INITIALISE_COMPLETE:
                                ContextID = 0;
                                NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                                ASSERT(ContextID);
                                
                                if ( ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus == 0 ) // SUCCESS
                                    {                               
                                    memset(&OemNdisEvent, 0, sizeof ( OEMNDISEVENT ));
                                    OemNdisEvent.type = OEM_NDIS_OPEN_CONNECTION;
                                    hr = NdisOemNdisOpenRequestFromHandle(m_rgpNdisEvents.lpNdisHandles[dwIndex], &RilNdisGprsContext);
                                    if ( hr == S_OK )
                                        {
                                        if ( RilNdisGprsContext.dwParams & RIL_PARAM_RILNDISGPRSCONTEXT_USERNAME )
                                            {
                                            StringCchCopyA( OemNdisEvent.event.open.psUsername, NDIS_USERNAME_MAX_LENGTH, AnsiString(RilNdisGprsContext.tszUserName) );
                                            StringCchCopyA( OemNdisEvent.event.open.psPassword, NDIS_PASSWORD_MAX_LENGTH, AnsiString(RilNdisGprsContext.tszPassword) );
                                            OemNdisEvent.event.open.dwUsernameLength = strlen(OemNdisEvent.event.open.psUsername);
                                            OemNdisEvent.event.open.dwPasswordLength = strlen(OemNdisEvent.event.open.psPassword);
                                            DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisNotifyThread  Username = %hs,  Length = %d,  Password = %hs  Length = %d  \r\n"), 
                                                    OemNdisEvent.event.open.psUsername, OemNdisEvent.event.open.dwUsernameLength,
                                                    OemNdisEvent.event.open.psPassword,OemNdisEvent.event.open.dwPasswordLength ));
                                            }
                                        OemNdisEvent.event.open.cid = ContextID;
                                        }
                                    if ( !NdisInterfaceTransmitEvent( m_rgpNdisEvents.lpNdisHandles[dwIndex], &OemNdisEvent) )
                                        {
                                        hr = E_FAIL;
                                        ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus = 1; // Set error
                                        }
                                   }
                                else
                                    {
                                    hr = E_FAIL;
                                    }
                               
                                if ( S_OK != hr )
                                    {
                                    OemNdisApiReference.eEventType= OEM_NDIS_OPEN_COMPLETE;
                                    OemNdisApiReference.dwStatus = ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus;
                                    OemNdisApiReference.ContextID = ContextID;
                                    OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                                    OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisApiReference.dwStatus;
                                    ProcessCommandFlag = m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );
                                    ASSERT(ProcessCommandFlag);
                                    NdisShutdownConnection ( m_rgpNdisEvents.lpNdisHandles[dwIndex] );
                                    ProcessNextContextRequest();
                                    }
                               break;
                    
                           case OEM_NDIS_OPEN_COMPLETE:     
                               ContextID = 0;
                               NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                               ASSERT(ContextID);
                               NdisRilCallbacksFromCID( ContextID,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );                                       
                               if ( NULL == pfnRilStatusCallback )
                                   {
                                   ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus = RIL_E_CANCELLED;
                                   }
                               OemNdisApiReference.dwStatus = ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus;
                               OemNdisApiReference.eEventType= OEM_NDIS_OPEN_COMPLETE;
                               OemNdisApiReference.ContextID = ContextID;
                               OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisApiReference.dwStatus;
                               OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                               if ( ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus != 0 ) // FAILURE!
                               {
                                   ProcessCommandFlag = m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );  
                                   NdisShutdownConnection ( m_rgpNdisEvents.lpNdisHandles[dwIndex] );
                                   ProcessNextContextRequest();
                                   ASSERT(ProcessCommandFlag);                
                               }
                               else
                               {
                                   RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
                                   RilGprsContexActivated.fActivated = TRUE;
                                   RilGprsContexActivated.dwContextID = ContextID;
                                   RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEACT;
                                   ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
                               }
                               break;
                    
                           case OEM_NDIS_CLOSE_COMPLETE:  
                               ContextID = 0;
                               NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                               ASSERT(ContextID);
                               OemNdisApiReference.dwStatus = ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus;
                               OemNdisApiReference.eEventType= OEM_NDIS_CLOSE_COMPLETE;
                               OemNdisApiReference.ContextID = ContextID;
                               ProcessCommandFlag=m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );
                               NdisShutdownConnection ( m_rgpNdisEvents.lpNdisHandles[dwIndex] ); 
                               ProcessNextContextRequest();
                               break;
                               
                           case OEM_NDIS_REGISTRATION_STATUS:
                               DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  fGsmRegistered = %x, fGprsAttached = %x\r\n"), 
                                                         ReceiveOemNdisEvent.event.regStatus.fGsmRegistered, 
                                                         ReceiveOemNdisEvent.event.regStatus.fGprsAttached));
                               if ( FALSE == ReceiveOemNdisEvent.event.regStatus.fGprsAttached )
                               {
                                   if ( S_OK  == NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID) )
                                   {
                                       RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
                                       RilGprsContexActivated.fActivated = FALSE;
                                       RilGprsContexActivated.dwContextID = ContextID;
                                       RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_NWDEACT;
                                       ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
                                   }
                               }
                               break;
                               
                           case OEM_NDIS_BUFFER_STATUS:
                               DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS:  reason = %x, maxRxBuffers = %x rxBuffersInUse = %0x  maxTxBuffers = %0x txBuffersQueued = %0x \r\n"), 
                                                     ReceiveOemNdisEvent.event.bufferStatus.eReason, 
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwMaxRxBuffers,
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwRxBuffersInUse,
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwMaxTxBuffers,
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwTxBuffersQueued));
                               break;
                
                           case OEM_NDIS_NULL_EVENT: 
                               break;
                    
                       }
                   }
               }while ( fMoreEvents && fSuccess );
        }
    }while(1);

Exit:
    CloseHandle ( m_rgpNdisEvents.hNdisAddChannelEvent );
    
    return 0;
}

// **************************************************************************
// Function Name: NdisAddNotifyEvent
// 
// Purpose:       Locate an event created when an NDIS channel is opened. Add the
//                event to the update event list and signal the NDIS thread that
//                there are more events to wait for.
//
// Arguments:     IN Handle of the newly created NDIS channel.
//
// Return Values: S_OK if event was found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************

HRESULT CRilNDISTTPCOM:: NdisAddNotifyEvent(HANDLE hNdisHandle)
{
    TCHAR *pszEventName = NULL;
    HANDLE hHandle;
    HRESULT hr = E_FAIL;

    if ( hNdisHandle )
    {   
        pszEventName = new TCHAR[MAX_NDIS_EVENT_NAME];
    
        if ( pszEventName )
        {
            hr = StringCchPrintf(pszEventName, MAX_NDIS_EVENT_NAME, TEXT("%s%p"),
                                 TEXT(NDIS_EVENT_PREFIX), hNdisHandle);
        
            if ( SUCCEEDED(hr) )
            {
                hr = E_FAIL; // Reset 
                // Find the event associated with this handle suffix
                hHandle = CreateEvent( NULL, FALSE, FALSE, pszEventName);
                if ( hHandle )
                {  
                    // The event should already exist. If it doesn't, something is wrong.
                    if ( ERROR_ALREADY_EXISTS == GetLastError ())
                    {                
                        EnterCriticalSection(&m_csNdisEventList);
                        // Now we can add the event to the structure and notify the event thread
                        for (int i=0;i<MAX_OEMNDIS_EVENTS;i++)
                        {
                          if (!m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] )
                          {
                            hr = S_OK;
                            m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] = hHandle; // store the handles used for WaitForMultipleObjects
                            m_rgpNdisEvents.lpNdisHandles[i] = hNdisHandle; // store the quick (NDIS) handle parallel to it.
                            SetEvent(m_rgpNdisEvents.hNdisAddChannelEvent );
                            break;
                          }
                        }
                        LeaveCriticalSection(&m_csNdisEventList);
                    }
                    if ( S_OK != hr )
                    {
                        CloseHandle(hHandle);
                    }
                }
            }
            delete [] pszEventName;
        }
    }
    return hr;    
}


// **************************************************************************
// Function Name:   NdisRemoveNotifyEvent
// 
// Purpose:    Locate and remove the event associated with an NDIS channel. This
//             function is called when when the NDIS channel is closed. Remove
//             the event from the update list and signal the NDIS thread that 
//             the list is updated. After the event is removed from the list,
//             shift the list to eliminate any holes.
//                                 
//
// Arguments:  handle associated with the NDIS channel that has been closed.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************  
HRESULT CRilNDISTTPCOM:: NdisRemoveNotifyEvent(HANDLE hNdisHandle)
{
    HRESULT hr = S_OK;
    BOOL fFound = FALSE;
    
    if ( !hNdisHandle ) 
    {
      hr = E_FAIL;
      goto Error;
    }

    EnterCriticalSection(&m_csNdisEventList);
    
    for (int i=0;i<MAX_OEMNDIS_EVENTS;i++)
    {
        // We search the NDIS handle list, not the event handle list.
        if ( m_rgpNdisEvents.lpNdisHandles[i] == hNdisHandle ) 
        {
            // We don't worry about destroying the object because NDIS radio support does it for us.
            m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] = 0;  // the event handle.
            m_rgpNdisEvents.lpNdisHandles[i] = 0;          // the ndis handle.
            m_rgpNdisEvents.nObjectCount--; // One more thing to wait for.
            fFound = TRUE;
            break;
        }
    }
    
    // Now we have a list of object handles that may have a hole in it. Make a new list without holes.
    // Now we can add the event to the structure and notify the event thread
    for (int i=0;i<MAX_OEMNDIS_EVENTS-1;i++)
    {
        if ( !m_rgpNdisEvents.lpNdisEventHandlesUpdate[i]  )
        {
            if ( m_rgpNdisEvents.lpNdisEventHandlesUpdate[i+1] )
            {
                m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] = m_rgpNdisEvents.lpNdisEventHandlesUpdate[i+1] ;
                m_rgpNdisEvents.lpNdisHandles[i] = m_rgpNdisEvents.lpNdisHandles[i+1];
                m_rgpNdisEvents.lpNdisHandles[i+1] = 0;
                m_rgpNdisEvents.lpNdisEventHandlesUpdate[i+1] = 0;
            }
        }
    }
    
    // Notify the NDIS thread to update the waiting list.
    if ( fFound )
        SetEvent(m_rgpNdisEvents.hNdisAddChannelEvent );
    
    LeaveCriticalSection(&m_csNdisEventList);

Error:
    return hr;
}

// **************************************************************************
// Function Name: NdisInterfaceInitialise
// 
// Purpose:       Emulate a single NDIS Channel
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HANDLE  CRilNDISTTPCOM::NdisInterfaceInitialise (DWORD index)
{
    if ( index >= FIRST_VALID_NDIS_INDEX )
    {
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisInterfaceInitialise index = %x\r\n"), index));
        return(g_pfnNdisInitialize ( index));
    }
    else
        return NULL;
};

// **************************************************************************
// Function Name:  NdisInterfaceShutdown
// 
// Purpose:        Emulate a single NDIS Channel
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDISTTPCOM::NdisInterfaceShutdown (HANDLE handle)
{
    if ( handle )
    {
        DEBUGMSG( ZONE_NDIS, (TEXT("RILNDIS: NdisInterfaceShutdown handle = %x\r\n"),handle));
        return(g_pfnNdisShutdown(handle));
    }
    else
        return FALSE;
};

// **************************************************************************
// Function Name: NdisInterfaceReceiveEvent
// 
// Purpose:       Emulate a single NDIS Channel
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDISTTPCOM::NdisInterfaceReceiveEvent(HANDLE handle, OEMNDISEVENT *event_p, BOOL *moreEvents_p)
{
    if ( handle && event_p && moreEvents_p )    
    {
        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisInterfaceReceiveEvent handle = %x\r\n"),handle));
        return(g_pfnNdisReceiveEvent(handle,event_p,moreEvents_p));
    }
    else
        return FALSE;
};

// **************************************************************************
// Function Name:   NdisInterfaceTransmitEvent
// 
// Purpose:         Simulate the operation of and NDIS radio.
//
// Arguments:       IN handle - unique to NDIS channel opened.
//                  IN event_p - defintion of the event being sent.
//
// Return Values:   TRUE if successful.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
 BOOL CRilNDISTTPCOM::NdisInterfaceTransmitEvent(HANDLE handle, const OEMNDISEVENT *event_p)
{
    BOOL fReturn = FALSE;
    
    if ( handle && event_p )
    {
        fReturn = g_pfnNdisTransmitEvent(handle,event_p);
    
        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisInterfaceTransmitEvent handle = %x Return = %x\r\n"),handle, fReturn));
    }
    
    return fReturn;
};
 
// **************************************************************************
// Function Name:   NdisAllocateCommand
// 
// Purpose:    Allocate an asynchronous command. This method is used
//             when the response will key off of the event type.
//
// Arguments:  IN eType: expected event.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDISTTPCOM::NdisAllocateCommand (  OEMNDISEVENTTYPE eType, HANDLE hNdisChannelHandle, LPVOID lpCrilInstance, HRESULT CommandID, DWORD ContextID )
{
    if ( hNdisChannelHandle && lpCrilInstance )
    {
        OEMNDISAPIREFERENCE OemNdisApiReference;
        memset(&OemNdisApiReference,0,sizeof(OemNdisApiReference));
    
        OemNdisApiReference.dwCrilInstance = lpCrilInstance;
        OemNdisApiReference.eEventType = eType;
        OemNdisApiReference.hNdisHandle = hNdisChannelHandle;
        OemNdisApiReference.hrCommandID = CommandID; 
        OemNdisApiReference.ContextID = ContextID;
        m_NdisAsyncCommandList.AddCommand ( &OemNdisApiReference );
    }
}

void
CRilNDISTTPCOM:: NdisRemoveCommand ( HRESULT hrCommandID )
{
    if ( hrCommandID > 0)
        {
        m_NdisAsyncCommandList.RemoveCommand ( hrCommandID );
        }
}

inline void
CRilNDISTTPCOM::PdpContextLock()
{
    EnterCriticalSection(&m_PdpContextCriticalSection);
}

inline void
CRilNDISTTPCOM::PdpContextUnlock()
{
    LeaveCriticalSection(&m_PdpContextCriticalSection);
}

inline BOOL
CRilNDISTTPCOM::IsPdpContextLocked()
{
    return m_PdpContextCriticalSection.OwnerThread == (HANDLE)GetCurrentThreadId();
}

PPDP_CONTEXT_ENTRY
CRilNDISTTPCOM::PdpContextEntryGet()
{
    PPDP_CONTEXT_ENTRY pEntry = NULL;

    ASSERT(IsPdpContextLocked());

    if (!IsListEmpty(&m_PdpContextFreeList))
        {
        pEntry = (PPDP_CONTEXT_ENTRY)RemoveHeadList(&m_PdpContextFreeList);
        }

    if (pEntry == NULL)
        {
        pEntry = (PPDP_CONTEXT_ENTRY)LocalAlloc(LPTR, sizeof(*pEntry));
        }

    if ( pEntry )
        {
        pEntry->AsyncCommandID = 0;
        pEntry->pCRilInstance = NULL;
        memset(&pEntry->RilNdisGprsContext, 0, sizeof(pEntry->RilNdisGprsContext));
        }

    return pEntry;
}

void 
CRilNDISTTPCOM::DumpPdpContextList ( void )
{

    PPDP_CONTEXT_ENTRY pEntry;
    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //

    for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
         TRUE;
         pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
        {
        if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
            {
            break;
            }
        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: DumpPdpContextList  pPdpContextEntry = %x \r\n"), pEntry));
        }
    PdpContextUnlock();

}

void 
CRilNDISTTPCOM::AddPdpContextEntry( const RILNDISGPRSCONTEXT *pRilNdisGprsContext, HRESULT AsyncCommandID, PVOID pCRilInstance )
{
    PPDP_CONTEXT_ENTRY pEntry;
    PdpContextLock();
    pEntry = PdpContextEntryGet();
    ASSERT(pEntry);
    if ( pEntry )
        {
        pEntry->AsyncCommandID = AsyncCommandID;
        pEntry->pCRilInstance = pCRilInstance;
        CeSafeCopyMemory(&pEntry->RilNdisGprsContext,pRilNdisGprsContext,sizeof(pEntry->RilNdisGprsContext));
        InsertTailList(&m_PdpContextPendingList, &pEntry->Node);
        }
    PdpContextUnlock();
    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: AddPdpContextEntry  pPdpContextEntry = %x \r\n"), pEntry));
    DumpPdpContextList();
}

void 
CRilNDISTTPCOM::RemovePdpContextEntry( PPDP_CONTEXT_ENTRY pPdpContextEntry )
{
    PPDP_CONTEXT_ENTRY pEntry;
    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //

    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: RemovePdpContextEntry  pPdpContextEntry = %x \r\n"), pPdpContextEntry));

    for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
         TRUE;
         pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
        {
        if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
            {
            break;
            }

        if ( pEntry == pPdpContextEntry )
            {
            RemoveEntryList(&pEntry->Node);
            InsertTailList(&m_PdpContextFreeList, &pEntry->Node);
            break;
            }
        }
    PdpContextUnlock();
    DumpPdpContextList();

}


PPDP_CONTEXT_ENTRY
CRilNDISTTPCOM::GetNextPdpContextEntry(  void )
{
    PPDP_CONTEXT_ENTRY pEntry;
    PPDP_CONTEXT_ENTRY pReturnEntry = NULL;
    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
         TRUE;
         pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
        {
        if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
            {
            break;
            }
        else
            {
            pReturnEntry = pEntry;
            break;
            }
        }
    PdpContextUnlock();

    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: GetNextPdpContextEntry  pReturnEntry = %x \r\n"), pReturnEntry));
    return pReturnEntry;
}

BOOL            
CRilNDISTTPCOM::IsPdpContextListEmpty( void )
{
    BOOL fReturn = IsListEmpty(&m_PdpContextPendingList);
    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: IsPdpContextListEmpty = %s \r\n"), 
            fReturn ? L"TRUE" : L"FALSE"));

    return fReturn;
}

void 
CRilNDISTTPCOM::RemoveAndSignalAllPdpContextEntries ( DWORD ContextID )
{
    PPDP_CONTEXT_ENTRY pEntry;
    DWORD ResultDetails;
    BOOL    ExitNow = FALSE;

    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
             TRUE;
             pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
            {
            if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
                {
                ExitNow = TRUE;
                break;
                }
            if ( pEntry->RilNdisGprsContext.dwContextID== ContextID )
                {
                ResultDetails = RIL_RESULT_ERROR;
                ((CRilInstanceHandle*)pEntry->pCRilInstance)->Notify(RIL_RESULT_ERROR,  pEntry->AsyncCommandID, &ResultDetails, sizeof(ResultDetails));
                RemoveEntryList(&pEntry->Node);
                InsertTailList(&m_PdpContextFreeList, &pEntry->Node);
                break;
                }
            }
        }
    PdpContextUnlock();

}

void            
CRilNDISTTPCOM::ProcessNextContextRequest(void)
{
    PPDP_CONTEXT_ENTRY  pPdpContextEntry=NULL;
    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessNextContextRequest \r\n")));
    LockPdpContextList();
    pPdpContextEntry = GetNextPdpContextEntry();
    RemovePdpContextEntry(pPdpContextEntry);
    pPdpContextEntry = GetNextPdpContextEntry();
    while (pPdpContextEntry)
        { 
        ASSERT((pPdpContextEntry->AsyncCommandID > 0));
        ASSERT(pPdpContextEntry->pCRilInstance);
        HRESULT Result=E_FAIL;  
        DWORD dwIndex;
        HANDLE hNdisChannelHandle = NULL;
        OEMNDISEVENT sEvent;
        memset(&sEvent, 0, sizeof(OEMNDISEVENT));

        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessNextContextRequest   cid = %x, Activation = %s pCRilInstance = %x AsyncCommandID = %x\r\n"), 
                pPdpContextEntry->RilNdisGprsContext.dwContextID, 
                pPdpContextEntry->RilNdisGprsContext.fContextActivation ? L"TRUE" : L"FALSE", 
                pPdpContextEntry->pCRilInstance,
                pPdpContextEntry->AsyncCommandID));

        if ( S_OK != NdisHandleFromCid( pPdpContextEntry->RilNdisGprsContext.dwContextID, &hNdisChannelHandle ))
            {
            hNdisChannelHandle = NULL; 
            }

       if ( !pPdpContextEntry->RilNdisGprsContext.fContextActivation )
            {
            if ( hNdisChannelHandle) 
                {
                sEvent.type = OEM_NDIS_CLOSE_CONNECTION;
                sEvent.event.open.cid = pPdpContextEntry->RilNdisGprsContext.dwContextID;
                NdisAllocateCommand ( OEM_NDIS_CLOSE_COMPLETE, hNdisChannelHandle, (LPVOID)pPdpContextEntry->pCRilInstance, pPdpContextEntry->AsyncCommandID, pPdpContextEntry->RilNdisGprsContext.dwContextID ); 
                if ( FALSE == NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
                    {
                    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessNextContextRequest  CLOSE handle = %x Call FAILED\r\n"), hNdisChannelHandle));
                    Result = E_FAIL;
                    NdisShutdownConnection ( hNdisChannelHandle );
                    }
                else
                    {
                    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessNextContextRequest  CLOSE handle = %x \r\n"), hNdisChannelHandle));
                    Result = S_OK;
                    }
                }
            else //  Error
                {
                Result = RIL_E_INVALIDCONTEXTSTATE; 
                }
            }
        else 
            {
            if ( NULL == hNdisChannelHandle) // This should normally be the case.
                {    
                // Check to see if there are any available to initialise.
                Sleep(1000);
                Result = NdisGetFreeNdisEntry( &dwIndex);
                if ( SUCCEEDED(Result) )
                    {
                    hNdisChannelHandle = NdisInterfaceInitialise (dwIndex);
                    if ( hNdisChannelHandle )
                        {
                        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessNextContextRequest  OPEN handle = %x \r\n"), hNdisChannelHandle));
                        NdisAddNdisEntry(pPdpContextEntry->RilNdisGprsContext.dwContextID,  dwIndex, hNdisChannelHandle, pPdpContextEntry->pCRilInstance, &pPdpContextEntry->RilNdisGprsContext ); 
                        NdisAllocateCommand ( OEM_NDIS_OPEN_COMPLETE, hNdisChannelHandle, (LPVOID)pPdpContextEntry->pCRilInstance, pPdpContextEntry->AsyncCommandID, pPdpContextEntry->RilNdisGprsContext.dwContextID);
                        NdisAddNotifyEvent(hNdisChannelHandle); 
                        }
                    else
                        {
                        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessNextContextRequest  OPEN FAILED \r\n")));
                        Result = E_FAIL;
                        }
                    }
                else
                    {
                    Result = RIL_E_MAXCONTEXTS;
                    }
                }  
             else // Error 
                {
                Result = RIL_E_INVALIDCONTEXTSTATE;
                }
            }

            // Process error condition
            if ( FAILED(Result) )
                {
                NdisRemoveCommand( pPdpContextEntry->AsyncCommandID );
                ((CRilInstanceHandle*)pPdpContextEntry->pCRilInstance)->Notify(RIL_RESULT_ERROR, 
                                                pPdpContextEntry->AsyncCommandID, 
                                                &Result, 
                                                sizeof(Result));
                RemovePdpContextEntry(pPdpContextEntry);
                pPdpContextEntry = GetNextPdpContextEntry();
                }
            else
                {
                pPdpContextEntry = NULL;
                }

        }
    UnlockPdpContextList();
}


CRilInstanceNDISTTPCOM::CRilInstanceNDISTTPCOM()
{
    m_pCrilInstance = NULL;
}

CRilInstanceNDISTTPCOM::~CRilInstanceNDISTTPCOM()
{
    m_pCrilInstance = NULL;
}

void CRilInstanceNDISTTPCOM::Init(PVOID hInstance)
{
    m_pCrilInstance = hInstance;
}

// **************************************************************************
// Function Name: NdisSendPacket
// 
// Purpose:       Add async command and make NDIS call.
//
// Arguments:     IN ndis packet pointer
//
// Return Values:   E_FAIL if parameter or immediate failure detected.
//                  Command result value for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilInstanceNDISTTPCOM::NdisSendPacket( RILNDISPACKET* lpNdisPacket ) 
{
   HRESULT hr=E_FAIL ;
   CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();

   NdisPacketTest ( lpNdisPacket );
    
   if ( pCrilHandle )
   {
       HANDLE hNdisChannelHandle = NULL;
       OEMNDISEVENT sEvent;
       memset(&sEvent, 0, sizeof(OEMNDISEVENT));

       CRilNDISTTPCOM* pCRilNdis = static_cast<CRilNDISTTPCOM*>(pCrilHandle->m_pCRilNdis);
   
       // Retrieve the handle from the Context ID. 
       if ( S_OK != pCRilNdis->NdisHandleFromCid( lpNdisPacket->dwContextId, &hNdisChannelHandle ))
            hNdisChannelHandle = NULL;
        
       DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSendPacket Packet = %x, ContextID = %x, NdisChannelHandle = %x \r\n"), lpNdisPacket, lpNdisPacket->dwContextId, hNdisChannelHandle));    
       if (hNdisChannelHandle)
       {
            sEvent.type = OEM_NDIS_TX_PACKET;
            sEvent.event.packet_p = lpNdisPacket; 
            
            DumpPacket(lpNdisPacket);

            if ( pCRilNdis->NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
            {
                hr = S_OK;
            }
        }
   }

   return hr;
}



// **************************************************************************
// Function Name: NdisSetGPRSContextActivated
// 
// Purpose:      Bring the GRPS context up or down depending on input.
//
// Arguments:    IN structure defining the connection details.
//
// Return Values:  E_FAIL if error detected.
//                 Command result for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilInstanceNDISTTPCOM::NdisSetGPRSContextActivated (const RILNDISGPRSCONTEXT *lpNdisSetGprsContextActivated )
{
    HRESULT Result=E_FAIL; 
    HRESULT AsyncCommandID = 0;
    CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();

    DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSetGPRSContextActivated  cid = %x, Activation = %s \r\n"), 
             lpNdisSetGprsContextActivated->dwContextID, lpNdisSetGprsContextActivated->fContextActivation ? L"TRUE" : L"FALSE" ));

    if (!pCrilHandle) {
        goto Error;
       }

    CRilNDISTTPCOM* pCRilNdis = static_cast<CRilNDISTTPCOM*>(pCrilHandle->m_pCRilNdis);

    if ( pCrilHandle && lpNdisSetGprsContextActivated && lpNdisSetGprsContextActivated->dwContextID)
        {
        DWORD dwIndex;
        HANDLE hNdisChannelHandle = NULL;
        OEMNDISEVENT sEvent;
        memset(&sEvent, 0, sizeof(OEMNDISEVENT));
        RILNDISGPRSCONTEXT NdisSetGprsContextActivated;
        CeSafeCopyMemory(&NdisSetGprsContextActivated, lpNdisSetGprsContextActivated, sizeof(NdisSetGprsContextActivated));


        // Force NULL-terminated string variables
        NdisSetGprsContextActivated.tszDNS1[ARRAY_LENGTH(NdisSetGprsContextActivated.tszDNS1)-1]= TCHAR ('\0');
        NdisSetGprsContextActivated.tszDNS2[ARRAY_LENGTH(NdisSetGprsContextActivated.tszDNS2)-1]= TCHAR ('\0');
        NdisSetGprsContextActivated.tszPassword[ARRAY_LENGTH(NdisSetGprsContextActivated.tszPassword)-1]= TCHAR ('\0');
        NdisSetGprsContextActivated.tszUserName[ARRAY_LENGTH(NdisSetGprsContextActivated.tszUserName)-1]= TCHAR ('\0');


        AsyncCommandID = pCrilHandle->GetNextCmdID();
        pCRilNdis->LockPdpContextList();
        if ( FALSE == pCRilNdis->IsPdpContextListEmpty())
            {
            DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSetGPRSContextActivated  Queueing Request Command ID = %x \r\n"), AsyncCommandID));
            Result = S_OK;
            pCRilNdis->AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
            }
        else 
            {
            if ( S_OK != pCRilNdis->NdisHandleFromCid( NdisSetGprsContextActivated.dwContextID, &hNdisChannelHandle ))
                {
                hNdisChannelHandle = NULL; 
                }

            if ( !NdisSetGprsContextActivated.fContextActivation ) // Deactivating
                {
                if ( hNdisChannelHandle) 
                    {
                    pCRilNdis->NdisRemoveCallbacks(NdisSetGprsContextActivated.dwContextID);
                    sEvent.type = OEM_NDIS_CLOSE_CONNECTION;
                    sEvent.event.open.cid = NdisSetGprsContextActivated.dwContextID;
                    pCRilNdis->NdisAllocateCommand ( OEM_NDIS_CLOSE_COMPLETE, hNdisChannelHandle, (LPVOID)m_pCrilInstance, AsyncCommandID, NdisSetGprsContextActivated.dwContextID ); 
                    if ( FALSE == pCRilNdis->NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
                        {
                        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSetGPRSContextActivated  CLOSE handle = %x Call FAILED\r\n"), hNdisChannelHandle));
                        Result = E_FAIL;
                        pCRilNdis->NdisShutdownConnection ( hNdisChannelHandle );
                        }
                    else
                        {
                        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSetGPRSContextActivated  CLOSE handle = %x \r\n"), hNdisChannelHandle));
                        Result = S_OK;
                        pCRilNdis->AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
                        }
                    }
                else //  Error
                    {
                    Result = RIL_E_INVALIDCONTEXTSTATE; 
                    }
                }
            else 
            /*  
            Activating:
            */  
                {
                if ( NULL == hNdisChannelHandle) // This should normally be the case.
                    {    
                    // Check to see if there are any available to initialise.
                    Result = pCRilNdis->NdisGetFreeNdisEntry( &dwIndex);
                    if ( SUCCEEDED(Result) )
                        {
                        hNdisChannelHandle = pCRilNdis->NdisInterfaceInitialise (dwIndex);
                        if ( hNdisChannelHandle )
                            {
                            DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSetGPRSContextActivated  OPEN handle = %x \r\n"), hNdisChannelHandle));
                            pCRilNdis->NdisAddNdisEntry(NdisSetGprsContextActivated.dwContextID,  dwIndex, hNdisChannelHandle, m_pCrilInstance, &NdisSetGprsContextActivated ); 
                            pCRilNdis->NdisAllocateCommand ( OEM_NDIS_OPEN_COMPLETE, hNdisChannelHandle, (LPVOID)m_pCrilInstance, AsyncCommandID, NdisSetGprsContextActivated.dwContextID);
                            pCRilNdis->NdisAddNotifyEvent(hNdisChannelHandle); 
                            pCRilNdis->AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
                            }
                        else
                            {
                            DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisSetGPRSContextActivated  OPEN FAILED \r\n")));
                            Result = E_FAIL;
                            }
                        }
                    else
                        {
                        Result = RIL_E_MAXCONTEXTS;
                        }
                    }  
                else // Error 
                    {
                    Result = RIL_E_INVALIDCONTEXTSTATE;
                    }
                }
            }
            pCRilNdis->UnlockPdpContextList();
        }


    if ( FAILED(Result) && AsyncCommandID )
        {
            pCRilNdis->NdisRemoveCommand( AsyncCommandID );
            AsyncCommandID = Result;
        }

Error:
    return AsyncCommandID;
}


// **************************************************************************
// Function Name: NdisReceivePacketDone
// 
// Purpose:      Notify the NDIS radio that the Rx packet has been consumed.
//
// Arguments:    IN Rx packet pointer
//
// Return Values:  E_FAIL if error detected.
//                 Command result for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT CRilInstanceNDISTTPCOM::NdisReceivePacketDone (RILNDISPACKET* lpNdisPacket)
{
    HRESULT hr = E_FAIL; 
    CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();
   
    if ( pCrilHandle && lpNdisPacket )
    {
        CRilNDISTTPCOM* pCRilNdis = static_cast<CRilNDISTTPCOM*>(pCrilHandle->m_pCRilNdis);

        OEMNDISEVENT sEvent;
        HANDLE       hNdisChannelHandle = NULL;
        memset(&sEvent, 0, sizeof(OEMNDISEVENT));
        
        // Retrieve the handle from the Context ID.
        if ( S_OK != pCRilNdis->NdisHandleFromCid( lpNdisPacket->dwContextId, &hNdisChannelHandle ))
            hNdisChannelHandle = NULL;

        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: NdisReceivePacketDone Packet = %x, ContextID = %x, NdisChannelHandle = %x \r\n"), lpNdisPacket, lpNdisPacket->dwContextId, hNdisChannelHandle));    

        sEvent.type = OEM_NDIS_RX_PACKET_COMPLETE;
        sEvent.event.packet_p = lpNdisPacket;
            
        if (hNdisChannelHandle)
        {
            if ( pCRilNdis->NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
            {
                hr = S_OK;
            }
        }
    }
    
     return hr;
}

// **************************************************************************
// Function Name: AsyncResponse
// 
// Purpose:       Initialization
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
AsyncResponse::AsyncResponse()
{
    InitializeCriticalSection(&m_cs);
    InitializeListHead(&m_AsyncPendingList);
    InitializeListHead(&m_AsyncFreeList);
}


// **************************************************************************
// Function Name:  ~AsyncResponse
// 
// Purpose:         Clean up.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
AsyncResponse::~AsyncResponse()
{
    PASYNCCOMMAND pEntry;
    BOOL ExitNow = FALSE;
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    AsyncListLock();
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
                {
                ExitNow = TRUE;
                break;
                }

            RemoveEntryList(&pEntry->Node);
            InsertTailList(&m_AsyncFreeList, &pEntry->Node);
            break;
            }
        }
    ExitNow = FALSE;
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PASYNCCOMMAND)(m_AsyncFreeList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncFreeList))
                {
                ExitNow = TRUE;
                break;
                }
            RemoveEntryList(&pEntry->Node);
            LocalFree ( pEntry );
            break;
            }
        }
    AsyncListUnlock();
}


void 
AsyncResponse::AddCommand ( OEMNDISAPIREFERENCE *lpOemNdisApiReference )
{

    PASYNCCOMMAND pEntry = NULL;

    if ( lpOemNdisApiReference )
        {
        AsyncListLock();
        if (!IsListEmpty(&m_AsyncFreeList))
            {
            pEntry = (PASYNCCOMMAND)RemoveHeadList(&m_AsyncFreeList);
            }

        if (pEntry == NULL)
            {
            pEntry = (PASYNCCOMMAND)LocalAlloc(LPTR, sizeof(*pEntry));
            }

        if ( pEntry )
            {
            memcpy(&pEntry->OemNdisApiReference, lpOemNdisApiReference, sizeof(OEMNDISAPIREFERENCE));
            }
        InsertTailList(&m_AsyncPendingList, &pEntry->Node);
        AsyncListUnlock();
        }
}

void 
AsyncResponse::RemoveCommand( HRESULT hrCommandID )
{
    if ( hrCommandID > 0 )
        {
        PASYNCCOMMAND pEntry;
  
        AsyncListLock();
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
                {
                DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: RemoveCommand  hrCommandID = %x NOT FOUND\r\n"), hrCommandID));
                break;
                }

            if ( pEntry->OemNdisApiReference.hrCommandID == hrCommandID )
                {
                RemoveEntryList(&pEntry->Node);
                InsertTailList(&m_AsyncFreeList, &pEntry->Node);
                break;
                }
            }
        AsyncListUnlock();
        }
}

BOOL
AsyncResponse::ProcessCommand ( const OEMNDISAPIREFERENCE *lpOemNdisApiReference )
{
   
    BOOL fReturn = FALSE;
    if ( lpOemNdisApiReference )
        {
        CRilInstanceHandle* hCrilInstance=NULL;
        PASYNCCOMMAND pEntry;
      

        AsyncListLock();  
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {

            fReturn = TRUE;

            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
            {
            DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessCommand Notify FAILED \r\n")));
            fReturn = FALSE;
            break;
            }

            if ( lpOemNdisApiReference->eEventType )
                {
                if ( (lpOemNdisApiReference->eEventType == pEntry->OemNdisApiReference.eEventType) &&
                    (lpOemNdisApiReference->hNdisHandle == pEntry->OemNdisApiReference.hNdisHandle))
                    {
                    hCrilInstance = (CRilInstanceHandle*)pEntry->OemNdisApiReference.dwCrilInstance;
                    if ( hCrilInstance )
                        {
                        DWORD dwResult = lpOemNdisApiReference->dwStatus ? RIL_RESULT_ERROR : RIL_RESULT_OK;
                        hCrilInstance->Notify(  dwResult, 
                                    pEntry->OemNdisApiReference.hrCommandID, 
                                    &lpOemNdisApiReference->RilNdisGprsContextResponse, 
                                    sizeof(lpOemNdisApiReference->RilNdisGprsContextResponse));
                        DEBUGMSG(ZONE_NDIS, (TEXT("RILNDIS: ProcessCommand Notify Command Status = %x Comamand ID = %0x \r\n"), 
                                    dwResult, 
                                    pEntry->OemNdisApiReference.hrCommandID));
                        }
                    RemoveEntryList(&pEntry->Node);
                    InsertTailList(&m_AsyncFreeList, &pEntry->Node);
                    break;
                    }
                }
            }
        AsyncListUnlock();
        }
    return fReturn;
}

void 
AsyncResponse::RemoveAllCommands( HANDLE hContextHandle )
{
    PASYNCCOMMAND pEntry;
    BOOL ExitNow = FALSE;
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    AsyncListLock();
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
                {
                ExitNow = TRUE;
                break;
                }
            if ( hContextHandle == pEntry->OemNdisApiReference.hNdisHandle )
                {
                RemoveEntryList(&pEntry->Node);
                InsertTailList(&m_AsyncFreeList, &pEntry->Node);
                break;
                }
            }
        }
    AsyncListUnlock();
}

inline void
AsyncResponse::AsyncListLock()
{
    EnterCriticalSection(&m_cs);
}

inline void
AsyncResponse::AsyncListUnlock()
{
    LeaveCriticalSection(&m_cs);
}

HRESULT PDD_GetRilNdisObject(__in const CRilHandle* pRilHandle, __out CRilNDIS*& pRilNdis)
{
    CRilNDISTTPCOM* pRilNdisTTPCOM = NULL;
    HRESULT hr = E_FAIL;

    if (!pRilHandle) {
        hr = E_INVALIDARG;
        goto Error;
        }

    pRilNdisTTPCOM = new CRilNDISTTPCOM();
    if (!pRilNdisTTPCOM) {
        hr = E_OUTOFMEMORY;
        goto Error;
        }
    else {
        pRilNdisTTPCOM->Init((PVOID)pRilHandle);
        }

    pRilNdis = pRilNdisTTPCOM;
    hr = S_OK;
    
Error:
    return hr;
}

HRESULT PDD_GetRilInstanceNdisObject(__in const CRilInstanceHandle* pRilInstanceHandle, __out CRilInstanceNDIS*& pRilInstanceNdis)
{
    CRilInstanceNDISTTPCOM* pRilInstanceNdisTTPCOM = NULL;
    HRESULT hr = E_FAIL;

    if (!pRilInstanceHandle) {
        hr = E_INVALIDARG;
        goto Error;
        }

    pRilInstanceNdisTTPCOM = new CRilInstanceNDISTTPCOM();
    if (!pRilInstanceNdisTTPCOM) {
        hr = E_OUTOFMEMORY;
        goto Error;
        }
    else {
        pRilInstanceNdisTTPCOM->Init((PVOID)pRilInstanceHandle);
        }

    pRilInstanceNdis = pRilInstanceNdisTTPCOM;
    hr = S_OK;
    
Error:
    return hr;
}

