//
// 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 "precomp.h"

class  CResponseTTPCOM : public CResponse
{
public:
    CResponseTTPCOM() {};
    ~CResponseTTPCOM() {};
    CResponseTTPCOM(const CResponseTTPCOM &Rsp) : CResponse(Rsp) {};

private:
    BOOL    ParseSTC(LPCSTR& rszPointer);
    BOOL    ParseNotificationOEM
          (
            LPCSTR& szPointer, 
            BOOL& fExpectCRLF,
            BOOL fDataOnNotificationPort
#ifdef RIL_WATSON_REPORT        
            , BOOL& fErrorNotification
#endif
          );
    BOOL    ParseSIMToolkitCmdOrRspOEM(LPCSTR& rszPointer, const DWORD dwNotifyCode, RILSIMTOOLKITCMD*& pCmd, DWORD& dwCmdSize);
    void      Post_ParseCPINOEM(char* szState); 
};

#define DEFAULT_COM_MASK (EV_RXCHAR | EV_RLSD | EV_ERR | EV_BREAK | EV_EVENT2)
#define ENCODING_TECHARSET ENCODING_GSMDEFAULT_UNICODE

// NDIS stuff

#ifdef __cplusplus
extern "C" {
#endif


typedef struct _PDP_CONTEXT_ENTRY
{
    LIST_ENTRY          Node;
    HRESULT             AsyncCommandID;
    RILNDISGPRSCONTEXT  RilNdisGprsContext; 
    PVOID               pCRilInstance;
} PDP_CONTEXT_ENTRY, *PPDP_CONTEXT_ENTRY;

typedef enum oemndiseventtype_tag
{
  OEM_NDIS_NULL_EVENT,             /* no event available                        */

  /* Events generated by OEM */

  OEM_NDIS_RX_PACKET,             /* Received a packet                          */
  OEM_NDIS_TX_PACKET_COMPLETE,    /* TX packet done - buffer can be reused      */
  OEM_NDIS_IP_CONFIGURATION,      /* NDIS configuration from network            */
  OEM_NDIS_XOFF,                  /* Please stop sending data to me             */
  OEM_NDIS_XON,                   /* Restart sending data to me                 */
  OEM_NDIS_INITIALISE_COMPLETE,   /*Initialise is now complete                  */
  OEM_NDIS_OPEN_COMPLETE,         /* Open is now complete                       */
  OEM_NDIS_CLOSE_COMPLETE,        /* Close is now complete                      */
  OEM_NDIS_REGISTRATION_STATUS,   /* Indicates whether network is available     */
  /* Events generated by RILGSM */

  OEM_NDIS_TX_PACKET,             /* Request to transmit a packet                */
  OEM_NDIS_RX_PACKET_COMPLETE,    /* RX packet complete - buffer can be reused   */
  OEM_NDIS_OPEN_CONNECTION,       /* open the connection                         */
  OEM_NDIS_CLOSE_CONNECTION,      /* close the connection                        */
  OEM_NDIS_REQUEST_BUFFER_STATUS, /* Request the current Rx buffer status        */
  OEM_NDIS_BUFFER_STATUS,         /* Indicates a significant change in buffers   */
  
  OEM_NDIS_NUMBER_OF_EVENTS
} OEMNDISEVENTTYPE;

typedef enum oemndisbufferstatusreason_tag
{
    OEM_BUFFER_STATUS_REASON_REQUEST,           /* Requested by host            */
    OEM_BUFFER_STATUS_REASON_RX_LOST_PACKET,    /* Lost packet due to insufficient packets */
    OEM_BUFFER_STATUS_REASON_RX_HIGH_WATERMARK, /* Rx buffer count exceeds high watermark (maxbuffers - 4) */
    OEM_BUFFER_STATUS_REASON_RX_LOW_WATERMARK   /* Rx buffer count less than 4  */
} OEMNDISBUFFERSTATUSREASON;
   
typedef struct oemndisbufferstatus_tag
{
    OEMNDISBUFFERSTATUSREASON eReason;
    DWORD                     dwMaxRxBuffers;
    DWORD                     dwRxBuffersInUse;
    DWORD                     dwMaxTxBuffers;
    DWORD                     dwTxBuffersQueued;
} OEMNDISBUFFERSTATUS;
    

typedef enum oemndiserroradditionalinfo_tag
{
    OEM_NO_ADDITIONAL_INFO = 0
} OEMNDISADDITIONALERROR;

typedef struct oemndisstatus_tag
{
    DWORD                   dwErrorStatus;
    OEMNDISADDITIONALERROR  AdditionalErrorInfo;
}  OEMNDISSTATUS;

typedef struct oemndisregistrationstatus_tag
{
    BOOL fGsmRegistered;
    BOOL fGprsAttached;
} OEMNDISREGSTATUS;
   
#define NO_SETTING 0xFFFFFFFF
#define NDIS_PASSWORD_MAX_LENGTH 64
#define NDIS_USERNAME_MAX_LENGTH 64

typedef struct oemndisopenrequest_tag
{
  DWORD                cid;  /* channel id to open */

  /* username and password to be used in connecting to the */
  /* GPRS network                                          */
  /* Optional - length == NO_SETTING means none            */

  DWORD          dwUsernameLength;
  char           psUsername[NDIS_USERNAME_MAX_LENGTH];
  DWORD          dwPasswordLength;
  char           psPassword[NDIS_PASSWORD_MAX_LENGTH];

} OEMNDISOPENREQUEST;

#define OEMNDIS_MAX_IP_ADDRESS_LENGTH 16

typedef struct oemndisipaddress_tag
{
  UCHAR length;         /* length 0 means unset */
  UCHAR address[16]; 
} OEMNDISIPADDRESS;

typedef struct oemndisconfiguration_tag
{
  OEMNDISIPADDRESS   ipAddress;
  OEMNDISIPADDRESS   primaryDnsAddress;
  OEMNDISIPADDRESS   secondaryDnsAddress;

  /* others to be defined??? */
} OEMNDISCONFIGURATION;

typedef struct oemndiseventtwo_tag
{
  OEMNDISEVENTTYPE            type;
  union
  {
     int                       null;
    LPRILNDISPACKET            packet_p;
    OEMNDISCONFIGURATION       configuration;
    OEMNDISSTATUS              NdisStatus;   
    OEMNDISOPENREQUEST         open;
    OEMNDISREGSTATUS           regStatus;
    OEMNDISBUFFERSTATUS        bufferStatus;
  } event;
} OEMNDISEVENT;

// Structure used for keeping track of the NDIS channels.
typedef struct oemndischannels_tag
{
    DWORD                dwCid;
    HANDLE               hNdisHandle;
    RILNDISGPRSCONTEXT   RilNdisGprsContext;
    VOID                *dwCrilInstance;
} OEMNDISCHANNELS;


typedef struct oemndisapireference_tag
{
    HRESULT           hrCommandID; // Used for storing the command ID.
    DWORD             ContextID;
    VOID              *dwCrilInstance;
    OEMNDISEVENTTYPE  eEventType;
    HANDLE            hNdisHandle;
    DWORD             dwStatus;
    RILNDISGPRSCONTEXTRESPONSE RilNdisGprsContextResponse;
} OEMNDISAPIREFERENCE;

#define NDIS_ADD_CHANNEL_EVENT_INDEX   0
#define NDIS_STOP_THREAD_INDEX         1
#define PERMANENT_OEMNDIS_EVENTS       2 // The events just listed.

#define NDIS_EVENT_PREFIX "TTPMUX_NDIS_NOTIFICATION_EVENT_"

#define FIRST_VALID_NDIS_INDEX 0
#define MAX_OEMNDIS_CHANNELS   3     // The number of gprs channels NDIS can open. It should be dynamic.
#define MAX_OEMNDIS_EVENTS     MAX_OEMNDIS_CHANNELS + PERMANENT_OEMNDIS_EVENTS     // The number of notifications (channels) from OEM plus 
// one for interprocess communication and one to kill the thread.

// Structure used with to keep track of events
typedef struct oemndiswaiteventobject_tag
{
    HANDLE      lpNdisEventHandlesPrimary[MAX_OEMNDIS_EVENTS];// Array of event handles waited on for WaitOnMultipleObjects
    HANDLE      lpNdisEventHandlesUpdate[MAX_OEMNDIS_EVENTS]; // Array of event handles waited on for WaitOnMultipleObjects
    HANDLE      lpNdisHandles[MAX_OEMNDIS_EVENTS];            // A array that shadows m_lpNdisEventHandles that maintains the OEM Ndis handles for convenience.
    INT         nObjectCount; // The number of objects used for WaitOnMultipleObjects. It refers to m_lpNdisEventHandles
    HANDLE      hNdisAddChannelEvent;
} OEMNDISWAITEVENTOBJECT;

typedef struct asyncresponse_tag
{
    LIST_ENTRY              Node;   
    OEMNDISAPIREFERENCE     OemNdisApiReference;  
} ASYNCCOMMAND, *PASYNCCOMMAND;

class AsyncResponse
{
public:
    AsyncResponse();
    ~AsyncResponse();
        
    void    AddCommand ( OEMNDISAPIREFERENCE *lpOemNdisApiReference );
    BOOL    ProcessCommand ( const OEMNDISAPIREFERENCE *lpOemNdisApiReference );
    void    RemoveCommand( HRESULT hrCommandID );
    void    RemoveAllCommands ( HANDLE hContextHandle  );

private:
    void    AsyncListLock(void);
    void    AsyncListUnlock(void);

    LIST_ENTRY    m_AsyncPendingList;
    LIST_ENTRY    m_AsyncFreeList;
    CRITICAL_SECTION  m_cs;
    
};

class CRilInstanceNDISTTPCOM : public CRilInstanceNDIS
{
public:
    CRilInstanceNDISTTPCOM();
    virtual ~CRilInstanceNDISTTPCOM();
    void Init(PVOID hInstance );
    HRESULT NdisSendPacket (RILNDISPACKET* lpNdisPacket);
    HRESULT NdisSetGPRSContextActivated (const RILNDISGPRSCONTEXT *lpNdisSetGprsContextActivated );
    HRESULT NdisReceivePacketDone (RILNDISPACKET* lpNdisPacket);

private:
    PVOID               m_pCrilInstance;
};

class CRilNDISTTPCOM : public CRilNDIS
{
public:
    CRilNDISTTPCOM();
    virtual ~CRilNDISTTPCOM();
    void NdisRemoveNdisEntry ( HANDLE hNdisHandle );
    void NdisAddCRilNdisEntry ( HANDLE hNdisHandle, PVOID pCRil  );
    void NdisAddNdisEntry ( DWORD cid, DWORD dwIndex,  HANDLE hHandle, VOID *hCrilInstance, const RILNDISGPRSCONTEXT *lpRilNdisGprsContext );
    void NdisRemoveCallbacks ( DWORD ContextID );
    void* NdisRilInstanceFromHandle( HANDLE hNdisHandle );
    void NdisRilCallbacksFromCID( DWORD ContextID, PVOID *pCallbackContext, RILNDISRECEIVECALLBACK *pfnRilReceiveCallback, RILNDISTRANSMITCALLBACK *pfnRilTransmitCallback, RILNDISSTATUSCALLBACK *pfnRilStatusCallback );
    HRESULT NdisGetFreeNdisEntry ( DWORD *lpIndex );
    DWORD NdisNotifyThread(void);
    HRESULT NdisAddNotifyEvent(HANDLE hNdisHandle);
    HRESULT NdisRemoveNotifyEvent(HANDLE hNdisHandle);
    HRESULT NdisCidFromHandle( HANDLE hNdisHandle, DWORD *lpCid );
    HRESULT NdisOemNdisOpenRequestFromHandle( HANDLE hNdisHandle, RILNDISGPRSCONTEXT *lpRilNdisGprsContext );
    HRESULT NdisCRilFromHandle( HANDLE hNdisHandle, PVOID *pCRil );
    HRESULT NdisHandleFromCid( DWORD cid, HANDLE *hNdisHandle );
    void NdisConvertConfig ( const OEMNDISCONFIGURATION* lpOemNdisConfig,  RILNDISIPCONFIG* lpRilNdisIpConfig);
    void NdisCloseAllConnections ( void );
    HANDLE NdisInterfaceInitialise (DWORD index);
    BOOL NdisInterfaceShutdown (HANDLE handle);
    BOOL NdisInterfaceReceiveEvent(HANDLE handle, OEMNDISEVENT *event_p, BOOL *moreEvents_p);
    BOOL NdisInterfaceTransmitEvent(HANDLE handle, const OEMNDISEVENT *event_p);
    void NdisShutdownConnection ( HANDLE hHandle );
    void NdisAllocateCommand ( OEMNDISEVENTTYPE eType, HANDLE hNdisChannelHandle, LPVOID lpCrilInstance, HRESULT CommandID, DWORD ContextID );
    void NdisRemoveCommand ( HRESULT hrCommandID );
    BOOL NdisStart ( HANDLE hCancelEvent );
    void NdisStop( void );
    void Init(PVOID pHandle );
    void AddPdpContextEntry( const RILNDISGPRSCONTEXT *pRilNdisGprsContext, HRESULT AsyncCommandID, PVOID pCRilInstance );
    void RemovePdpContextEntry( PPDP_CONTEXT_ENTRY pPdpContextEntry );
    BOOL IsPdpContextListEmpty( void );
    PPDP_CONTEXT_ENTRY GetNextPdpContextEntry(  void );
    void RemoveAndSignalAllPdpContextEntries ( DWORD ContextID );
    void LockPdpContextList(void){PdpContextLock();}
    void UnlockPdpContextList(void){PdpContextUnlock();}
    DWORD GetRadioXon(void){return m_fXon;}
    void ProcessNextContextRequest(void);

private:
    void PdpContextLock(void);
    void PdpContextUnlock(void);
    BOOL IsPdpContextLocked(void);
    PPDP_CONTEXT_ENTRY  PdpContextEntryGet(void);
    void DumpPdpContextList ( void );

    DWORD NdisCountEvents(void);

    OEMNDISCHANNELS                 m_rgpNdisChannels[MAX_OEMNDIS_CHANNELS]; // Array of ndis channels in use and available.
    HANDLE                          m_hNdisNotifyThread; // Handle returned from CreateThread
    DWORD                           m_dwNdisNotifyThreadID;
    OEMNDISWAITEVENTOBJECT          m_rgpNdisEvents; // all the info related to the events that NdisNotifyThread waits on.
    CRITICAL_SECTION                m_csNdisEventList;
    AsyncResponse                   m_NdisAsyncCommandList;
    PVOID                           m_pCrilHandle;
    HANDLE                          m_hCancelEvent;
    CRITICAL_SECTION                m_csConnectionList;
    LIST_ENTRY                      m_PdpContextPendingList;
    LIST_ENTRY                      m_PdpContextFreeList;
    CRITICAL_SECTION                m_PdpContextCriticalSection;
    DWORD                           m_fXon;
    HANDLE                          m_hCloseAllConnections;

};

#ifdef __cplusplus
}
#endif
