// ---------------------------------------------------------------------------------------
// halw.h
//
// Copyright (C) Microsoft Corporation
// ---------------------------------------------------------------------------------------

#ifndef _HALW_H_
#define _HALW_H_

// ---------------------------------------------------------------------------------------
// Definitions
// ---------------------------------------------------------------------------------------

typedef void (*PKDEFERRED_ROUTINE) (struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2);

typedef struct _KDPC {
    LIST_ENTRY              DpcListEntry;
    PKDEFERRED_ROUTINE      DeferredRoutine;
    PVOID                   DeferredContext;
    PVOID                   SystemArgument1;
    PVOID                   SystemArgument2;
} KDPC, *PKDPC, *PRKDPC;

typedef struct _KTIMER {
    LIST_ENTRY              TimerListEntry;
    KDPC *                  Dpc;
    DWORD                   DueTime;
    LONG                    Period;
} KTIMER, *PKTIMER, *PRKTIMER;

// ---------------------------------------------------------------------------------------
// CXnHal
// ---------------------------------------------------------------------------------------

void *  HalAlloc(size_t cb, ULONG tag);
void *  HalAllocZ(size_t cb, ULONG tag);
void    HalFree(void * pv);

class CXnHal : public CXnVoid
{
    // Definitions -----------------------------------------------------------------------

    #define KEVENT                      HANDLE
    #define PRKEVENT                    HANDLE
    #define EvtInit(prke)               prke = CreateEvent(NULL, FALSE, FALSE, NULL)
    #define EvtTerm(prke)               CloseHandle(prke)
    #define EvtSet(prke, pri)           SetEvent(prke)
    #define EvtClear(prke)              ResetEvent(prke)
    #define EvtRef(ke)                  ke
    #define EvtFromHandle(hEvent)       hEvent
    #define EvtDereference(prke)
    #define KeQuerySystemTime(x)        GetSystemTimeAsFileTime((LPFILETIME)(x))
    #define KeQueryTickCount()          GetTickCount()

    #define PASSIVE_LEVEL               0
    #define DISPATCH_LEVEL              1
    #define USER                        0x0001
    #define UDPC                        0x0002
    #define SDPC                        0x0004

    #define XC_ONLINE_IP_ADDRESS                0
    #define XC_ONLINE_SUBNET_ADDRESS            1
    #define XC_ONLINE_DEFAULT_GATEWAY_ADDRESS   2

#ifdef XNET_FEATURE_ASSERT
    #define TCHECK_(pXnBase, lev)       AssertSz((pXnBase)->HalThreadCheck(lev), "Not allowed to execute this code at the current level")
#else
    #define TCHECK_(pXnBase, lev)
#endif
    #define TCHECK(lev)                 TCHECK_(this, lev)

#ifdef XNET_FEATURE_ASSERT
    #define ICHECK_(pXnBase, lay, lev)  AssertSz((pXnBase)->TestInitFlag(INITF_##lay), "This layer is not initialized"); \
                                        AssertSz(!(pXnBase)->TestInitFlag(INITF_##lay##_TERM), "This layer has been terminated."); \
                                        TCHECK_(pXnBase, lev)
#else
    #define ICHECK_(pXnBase, lay, lev)
#endif
    #define ICHECK(lay, lev)            ICHECK_(this, lay, lev)

    #define HAL_DECLARE_NEW_DELETE(class) \
       INLINE void * operator new(size_t cb) { return(HalAllocZ(cb, PTAG_##class)); } \
       INLINE void operator delete(void * pv) { HalFree(pv); } \
       INLINE class() {} \
       INLINE ~class() {}

    // External --------------------------------------------------------------------------

public:

    HAL_DECLARE_NEW_DELETE(CXnHal)

    KIRQL               KeGetCurrentIrql();
    KIRQL               KeRaiseIrqlToDpcLevel();
    VOID                KeLowerIrql(KIRQL kirql);
    VOID                KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext);
    BOOLEAN             KeInsertQueueDpc(PRKDPC Dpc, PVOID SystemArgument1, PVOID SystemArgument2);
    BOOLEAN             KeRemoveQueueDpc(PRKDPC Dpc);
    VOID                KeInitializeTimer(PKTIMER Timer);
    BOOLEAN             KeSetTimerEx(PKTIMER Timer, LARGE_INTEGER DueTime, LONG Period, PKDPC Dpc);
    BOOLEAN             KeCancelTimer(PKTIMER Timer);

    BOOL                HalThreadCheck(UINT uiCheck);
    void                HalEnterDpc();
    BOOL                HalTryEnterDpc();
    void                HalLeaveDpc();

    UINT                HalRandGather(BYTE * pb, UINT cb);    

    void                SetInitFlag(DWORD dwFlag);
    INLINE BOOL         TestInitFlag(DWORD dwFlag)      { return(!!(_dwInitFlags & dwFlag)); }
    INLINE BOOL         EvtWait(PRKEVENT prke, UINT uiTimeout);

    #undef  HALAPI
    #define HALAPI(ret, fname, arglist, paramlist)  INLINE ret fname arglist { return(Hal##fname paramlist); }
    HALAPILIST()

protected:

    NTSTATUS            HalInit(XNetInitParams * pxnip);
    INLINE void         HalStart()  {};
    INLINE void         HalStop()   {};
    void                HalTerm();

    // Data ------------------------------------------------------------------------------

private:

    DWORD               _dwInitFlags;           // Initialization flags (INITF_*)

protected:

    char                _achXbox[64];           // Name of this xbox

};

INLINE BOOL CXnHal::EvtWait(PRKEVENT prke, UINT uiTimeout)
{
    BOOL fResult = (WaitForSingleObject(prke, uiTimeout ? uiTimeout : INFINITE) != WAIT_TIMEOUT);

    // We need to wait for the DPC thread to finish because it most likely just signalled
    // this event, and on the Xbox the user thread would not get a chance to run until
    // the dispatch routine returned.  On windows we don't simulate dispatch
    // exclusivity, so we just raise/lower to DPC here to make sure the dispatch call
    // that signalled the event is done.

    HalEnterDpc();
    HalLeaveDpc();

    return(fResult);
}

class CRaiseToDpc
{
public:
    CRaiseToDpc(CXnHal * pXnHal);
    ~CRaiseToDpc();
private:
    CRaiseToDpc();
    CXnHal *    _pXnHal;
    UINT        _irql;
};

#define RaiseToDpc()  CRaiseToDpc __RaiseToDpc__(this)

#define HalQueryTsc(pli)    QueryPerformanceCounter(pli)

#endif
