/*
 * This file contains general definitions for VirtIO network adapter driver,
 * common for both NDIS5 and NDIS6
 *
 * Copyright (c) 2008-2017 Red Hat, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met :
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and / or other materials provided with the distribution.
 * 3. Neither the names of the copyright holders nor the names of their contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#ifndef PARANDIS_56_COMMON_H
#define PARANDIS_56_COMMON_H

//#define PARANDIS_TEST_TX_KICK_ALWAYS

#if defined(OFFLOAD_UNIT_TEST)
#include <windows.h>
#include <stdio.h>

#define ETH_LENGTH_OF_ADDRESS       6
#define DoPrint(fmt, ...) printf(fmt##"\n", __VA_ARGS__)
#define DPrintf(a,b) DoPrint b
#define RtlOffsetToPointer(B,O)  ((PCHAR)( ((PCHAR)(B)) + ((ULONG_PTR)(O))  ))

#include "ethernetutils.h"
#endif //+OFFLOAD_UNIT_TEST

#if !defined(OFFLOAD_UNIT_TEST)

#if !defined(RtlOffsetToPointer)
#define RtlOffsetToPointer(Base,Offset)  ((PCHAR)(((PCHAR)(Base))+((ULONG_PTR)(Offset))))
#endif

#if !defined(RtlPointerToOffset)
#define RtlPointerToOffset(Base,Pointer)  ((ULONG)(((PCHAR)(Pointer))-((PCHAR)(Base))))
#endif


#include <ndis.h>
#include "osdep.h"
#include "kdebugprint.h"
#include "ethernetutils.h"
#include "virtio_pci.h"
#include "virtio.h"
#include "virtio_ring.h"
#include "IONetDescriptor.h"
#include "DebugData.h"

// those stuff defined in NDIS
//NDIS_MINIPORT_MAJOR_VERSION
//NDIS_MINIPORT_MINOR_VERSION
// those stuff defined in build environment
// PARANDIS_MAJOR_DRIVER_VERSION
// PARANDIS_MINOR_DRIVER_VERSION

#if !defined(NDIS_MINIPORT_MAJOR_VERSION) || !defined(NDIS_MINIPORT_MINOR_VERSION)
#error "Something is wrong with NDIS environment"
#endif

//define to see when the status register is unreadable(see ParaNdis_ResetVirtIONetDevice)
//#define VIRTIO_RESET_VERIFY

//define to if hardware raise interrupt on error (see ParaNdis_DPCWorkBody)
//#define VIRTIO_SIGNAL_ERROR

// define if qemu supports logging to static IO port for synchronization
// of driver output with qemu printouts; in this case define the port number
// #define VIRTIO_DBG_USE_IOPORT    0x99

// to be set to real limit later
#define MAX_RX_LOOPS    1000

// maximum number of virtio queues used by the driver
#define MAX_NUM_OF_QUEUES 3

/* The feature bitmap for virtio net */
#define VIRTIO_NET_F_CSUM   0   /* Host handles pkts w/ partial csum */
#define VIRTIO_NET_F_GUEST_CSUM 1   /* Guest handles pkts w/ partial csum */
#define VIRTIO_NET_F_MAC    5   /* Host has given MAC address. */
#define VIRTIO_NET_F_GSO    6   /* Host handles pkts w/ any GSO type */
#define VIRTIO_NET_F_GUEST_TSO4 7   /* Guest can handle TSOv4 in. */
#define VIRTIO_NET_F_GUEST_TSO6 8   /* Guest can handle TSOv6 in. */
#define VIRTIO_NET_F_GUEST_ECN  9   /* Guest can handle TSO[6] w/ ECN in. */
#define VIRTIO_NET_F_GUEST_UFO  10  /* Guest can handle UFO in. */
#define VIRTIO_NET_F_HOST_TSO4  11  /* Host can handle TSOv4 in. */
#define VIRTIO_NET_F_HOST_TSO6  12  /* Host can handle TSOv6 in. */
#define VIRTIO_NET_F_HOST_ECN   13  /* Host can handle TSO[6] w/ ECN in. */
#define VIRTIO_NET_F_HOST_UFO   14  /* Host can handle UFO in. */
#define VIRTIO_NET_F_MRG_RXBUF  15  /* Host can handle merged Rx buffers and requires bigger header for that. */
#define VIRTIO_NET_F_STATUS     16
#define VIRTIO_NET_F_CTRL_VQ    17      /* Control channel available */
#define VIRTIO_NET_F_CTRL_RX    18      /* Control channel RX mode support */
#define VIRTIO_NET_F_CTRL_VLAN  19      /* Control channel VLAN filtering */
#define VIRTIO_NET_F_CTRL_RX_EXTRA 20   /* Extra RX mode control support */

#define VIRTIO_NET_S_LINK_UP    1       /* Link is up */

#define VIRTIO_NET_INVALID_INTERRUPT_STATUS     0xFF

#define PARANDIS_MULTICAST_LIST_SIZE        32
#define PARANDIS_MEMORY_TAG                 '5muQ'
#define PARANDIS_FORMAL_LINK_SPEED          (pContext->ulFormalLinkSpeed)
#define PARANDIS_MAXIMUM_TRANSMIT_SPEED     PARANDIS_FORMAL_LINK_SPEED
#define PARANDIS_MAXIMUM_RECEIVE_SPEED      PARANDIS_FORMAL_LINK_SPEED
#define PARANDIS_MIN_LSO_SEGMENTS           2
// reported
#define PARANDIS_MAX_LSO_SIZE               0xF800

#define PARANDIS_UNLIMITED_PACKETS_TO_INDICATE  (~0ul)

extern VirtIOSystemOps ParaNdisSystemOps;

typedef enum _tagInterruptSource
{
    isControl  = VIRTIO_PCI_ISR_CONFIG,
    isReceive  = 0x10,
    isTransmit = 0x20,
    isUnknown  = 0x40,
    isBothTransmitReceive = isReceive | isTransmit,
    isAny      = isReceive | isTransmit | isControl | isUnknown,
    isDisable  = 0x80
}tInterruptSource;

static const ULONG PARANDIS_PACKET_FILTERS =
    NDIS_PACKET_TYPE_DIRECTED |
    NDIS_PACKET_TYPE_MULTICAST |
    NDIS_PACKET_TYPE_BROADCAST |
    NDIS_PACKET_TYPE_PROMISCUOUS |
    NDIS_PACKET_TYPE_ALL_MULTICAST;

typedef VOID (*ONPAUSECOMPLETEPROC)(VOID *);


typedef enum _tagSendReceiveState
{
    srsDisabled = 0,        // initial state
    srsPausing,
    srsEnabled
} tSendReceiveState;

typedef struct _tagBusResource {
    NDIS_PHYSICAL_ADDRESS BasePA;
    ULONG                 uLength;
    PVOID                 pBase;
    BOOLEAN               bPortSpace;
    BOOLEAN               bUsed;
} tBusResource;

typedef struct _tagAdapterResources
{
    tBusResource PciBars[PCI_TYPE0_ADDRESSES];
    ULONG Vector;
    ULONG Level;
    KAFFINITY Affinity;
    ULONG InterruptFlags;
} tAdapterResources;

typedef enum _tagOffloadSettingsBit
{
    osbT4IpChecksum = (1 << 0),
    osbT4TcpChecksum = (1 << 1),
    osbT4UdpChecksum = (1 << 2),
    osbT4TcpOptionsChecksum = (1 << 3),
    osbT4IpOptionsChecksum = (1 << 4),
    osbT4Lso = (1 << 5),
    osbT4LsoIp = (1 << 6),
    osbT4LsoTcp = (1 << 7),
    osbT4RxTCPChecksum = (1 << 8),
    osbT4RxTCPOptionsChecksum = (1 << 9),
    osbT4RxIPChecksum = (1 << 10),
    osbT4RxIPOptionsChecksum = (1 << 11),
    osbT4RxUDPChecksum = (1 << 12),
    osbT6TcpChecksum = (1 << 13),
    osbT6UdpChecksum = (1 << 14),
    osbT6TcpOptionsChecksum = (1 << 15),
    osbT6IpExtChecksum = (1 << 16),
    osbT6Lso = (1 << 17),
    osbT6LsoIpExt = (1 << 18),
    osbT6LsoTcpOptions = (1 << 19),
    osbT6RxTCPChecksum = (1 << 20),
    osbT6RxTCPOptionsChecksum = (1 << 21),
    osbT6RxUDPChecksum = (1 << 22),
    osbT6RxIpExtChecksum = (1 << 23),
}tOffloadSettingsBit;

typedef struct _tagOffloadSettingsFlags
{
    ULONG fTxIPChecksum     : 1;
    ULONG fTxTCPChecksum    : 1;
    ULONG fTxUDPChecksum    : 1;
    ULONG fTxTCPOptions     : 1;
    ULONG fTxIPOptions      : 1;
    ULONG fTxLso            : 1;
    ULONG fTxLsoIP          : 1;
    ULONG fTxLsoTCP         : 1;
    ULONG fRxIPChecksum     : 1;
    ULONG fRxTCPChecksum    : 1;
    ULONG fRxUDPChecksum    : 1;
    ULONG fRxTCPOptions     : 1;
    ULONG fRxIPOptions      : 1;
    ULONG fTxTCPv6Checksum  : 1;
    ULONG fTxUDPv6Checksum  : 1;
    ULONG fTxTCPv6Options   : 1;
    ULONG fTxIPv6Ext        : 1;
    ULONG fTxLsov6          : 1;
    ULONG fTxLsov6IP        : 1;
    ULONG fTxLsov6TCP       : 1;
    ULONG fRxTCPv6Checksum  : 1;
    ULONG fRxUDPv6Checksum  : 1;
    ULONG fRxTCPv6Options   : 1;
    ULONG fRxIPv6Ext        : 1;
}tOffloadSettingsFlags;


typedef struct _tagOffloadSettings
{
    /* current value of enabled offload features */
    tOffloadSettingsFlags flags;
    /* load once, do not modify - bitmask of offload features, enabled in configuration */
    ULONG flagsValue;
    ULONG ipHeaderOffset;
    ULONG maxPacketSize;
}tOffloadSettings;

typedef struct _tagChecksumCheckResult
{
    union
    {
        struct
        {
            ULONG   TcpFailed       :1;
            ULONG   UdpFailed       :1;
            ULONG   IpFailed        :1;
            ULONG   TcpOK           :1;
            ULONG   UdpOK           :1;
            ULONG   IpOK            :1;
        } flags;
        ULONG value;
    };
}tChecksumCheckResult;

/*
for simplicity, we use for NDIS5 the same statistics as native NDIS6 uses
*/
typedef struct _tagNdisStatistics
{
    ULONG64                     ifHCInOctets;
    ULONG64                     ifHCInUcastPkts;
    ULONG64                     ifHCInUcastOctets;
    ULONG64                     ifHCInMulticastPkts;
    ULONG64                     ifHCInMulticastOctets;
    ULONG64                     ifHCInBroadcastPkts;
    ULONG64                     ifHCInBroadcastOctets;
    ULONG64                     ifInDiscards;
    ULONG64                     ifInErrors;
    ULONG64                     ifHCOutOctets;
    ULONG64                     ifHCOutUcastPkts;
    ULONG64                     ifHCOutUcastOctets;
    ULONG64                     ifHCOutMulticastPkts;
    ULONG64                     ifHCOutMulticastOctets;
    ULONG64                     ifHCOutBroadcastPkts;
    ULONG64                     ifHCOutBroadcastOctets;
    ULONG64                     ifOutDiscards;
    ULONG64                     ifOutErrors;
}NDIS_STATISTICS_INFO;

typedef PNDIS_PACKET tPacketType;
typedef PNDIS_PACKET tPacketHolderType;
typedef PNDIS_PACKET tPacketIndicationType;

typedef struct _tagNdisOffloadParams
{
    UCHAR   IPv4Checksum;
    UCHAR   TCPIPv4Checksum;
    UCHAR   UDPIPv4Checksum;
    UCHAR   LsoV1;
    UCHAR   LsoV2IPv4;
    UCHAR   TCPIPv6Checksum;
    UCHAR   UDPIPv6Checksum;
    UCHAR   LsoV2IPv6;
}NDIS_OFFLOAD_PARAMETERS;

//#define UNIFY_LOCKS

typedef struct _tagOurCounters
{
    UINT nReusedRxBuffers;
    UINT nPrintDiagnostic;
    ULONG64 prevIn;
    UINT nRxInactivity;
}tOurCounters;

typedef struct _tagMaxPacketSize
{
    UINT nMaxDataSize;
    UINT nMaxFullSizeOS;
    UINT nMaxFullSizeHwTx;
    UINT nMaxFullSizeHwRx;
}tMaxPacketSize;

typedef struct _tagCompletePhysicalAddress
{
    PHYSICAL_ADDRESS    Physical;
    PVOID               Virtual;
    ULONG               size;
    ULONG               IsCached        : 1;
    ULONG               IsTX            : 1;
} tCompletePhysicalAddress;

typedef struct _tagMulticastData
{
    ULONG                   nofMulticastEntries;
    UCHAR                   MulticastList[ETH_LENGTH_OF_ADDRESS * PARANDIS_MULTICAST_LIST_SIZE];
}tMulticastData;

typedef struct _tagIONetDescriptor {
    LIST_ENTRY listEntry;
    tCompletePhysicalAddress HeaderInfo;
    tCompletePhysicalAddress DataInfo;
    tPacketHolderType pHolder;
    PVOID ReferenceValue;
    UINT  nofUsedBuffers;
} IONetDescriptor, * pIONetDescriptor;

typedef void (*tReuseReceiveBufferProc)(void *pContext, pIONetDescriptor pDescriptor);

typedef struct _tagPARANDIS_ADAPTER
{
    NDIS_HANDLE             DriverHandle;
    NDIS_HANDLE             MiniportHandle;
    NDIS_EVENT              ResetEvent;
    tAdapterResources       AdapterResources;
    tBusResource            SharedMemoryRanges[MAX_NUM_OF_QUEUES];

    VirtIODevice            IODevice;
    BOOLEAN                 bIODeviceInitialized;
    ULONGLONG               ullHostFeatures;
    ULONGLONG               ullGuestFeatures;

    LARGE_INTEGER           LastTxCompletionTimeStamp;
#ifdef PARANDIS_DEBUG_INTERRUPTS
    LARGE_INTEGER           LastInterruptTimeStamp;
#endif
    BOOLEAN                 bConnected;
    BOOLEAN                 bEnableInterruptHandlingDPC;
    BOOLEAN                 bEnableInterruptChecking;
    BOOLEAN                 bDoInterruptRecovery;
    BOOLEAN                 bDoSupportPriority;
    BOOLEAN                 bDoHwPacketFiltering;
    BOOLEAN                 bUseScatterGather;
    BOOLEAN                 bBatchReceive;
    BOOLEAN                 bLinkDetectSupported;
    BOOLEAN                 bDoHardwareChecksum;
    BOOLEAN                 bDoGuestChecksumOnReceive;
    BOOLEAN                 bDoIPCheckTx;
    BOOLEAN                 bDoIPCheckRx;
    BOOLEAN                 bUseMergedBuffers;
    BOOLEAN                 bDoKickOnNoBuffer;
    BOOLEAN                 bSurprizeRemoved;
    BOOLEAN                 bUsingMSIX;
    BOOLEAN                 bUseIndirect;
    BOOLEAN                 bHasHardwareFilters;
    BOOLEAN                 bHasControlQueue;
    BOOLEAN                 bNoPauseOnSuspend;
    BOOLEAN                 bFastSuspendInProcess;
    BOOLEAN                 bResetInProgress;
    ULONG                   ulCurrentVlansFilterSet;
    tMulticastData          MulticastData;
    UINT                    uNumberOfHandledRXPacketsInDPC;
    NDIS_DEVICE_POWER_STATE powerState;
    LONG                    dpcReceiveActive;
    LONG                    counterDPCInside;
    LONG                    bDPCInactive;
    LONG                    InterruptStatus;
    ULONG                   ulPriorityVlanSetting;
    ULONG                   VlanId;
    ULONGLONG               ulFormalLinkSpeed;
    ULONG                   ulEnableWakeup;
    tMaxPacketSize          MaxPacketSize;
    ULONG                   nEnableDPCChecker;
    ULONG                   ulUniqueID;
    UCHAR                   PermanentMacAddress[ETH_LENGTH_OF_ADDRESS];
    UCHAR                   CurrentMacAddress[ETH_LENGTH_OF_ADDRESS];
    ULONG                   PacketFilter;
    ULONG                   DummyLookAhead;
    ULONG                   ulMilliesToConnect;
    ULONG                   nDetectedStoppedTx;
    ULONG                   nDetectedInactivity;
    ULONG                   nVirtioHeaderSize;
    /* send part */
#if !defined(UNIFY_LOCKS)
    NDIS_SPIN_LOCK          SendLock;
    NDIS_SPIN_LOCK          ReceiveLock;
#else
    union
    {
    NDIS_SPIN_LOCK          SendLock;
    NDIS_SPIN_LOCK          ReceiveLock;
    };
#endif
    NDIS_STATISTICS_INFO    Statistics;
    struct
    {
        ULONG framesCSOffload;
        ULONG framesLSO;
        ULONG framesIndirect;
        ULONG framesRxPriority;
        ULONG framesRxCSHwOK;
        ULONG framesRxCSHwMissedBad;
        ULONG framesRxCSHwMissedGood;
        ULONG framesFilteredOut;
    } extraStatistics;
    tOurCounters            Counters;
    tOurCounters            Limits;
    tSendReceiveState       SendState;
    tSendReceiveState       ReceiveState;
    ONPAUSECOMPLETEPROC     SendPauseCompletionProc;
    ONPAUSECOMPLETEPROC     ReceivePauseCompletionProc;
    tReuseReceiveBufferProc ReuseBufferProc;
    /* Net part - management of buffers and queues of QEMU */
    struct virtqueue *      NetControlQueue;
    tCompletePhysicalAddress ControlData;
    struct virtqueue *      NetReceiveQueue;
    struct virtqueue *      NetSendQueue;
    /* list of Rx buffers available for data (under VIRTIO management) */
    LIST_ENTRY              NetReceiveBuffers;
    UINT                    NetNofReceiveBuffers;
    /* list of Rx buffers waiting for return (under NDIS management) */
    LIST_ENTRY              NetReceiveBuffersWaiting;
    /* list of Tx buffers in process (under VIRTIO management) */
    LIST_ENTRY              NetSendBuffersInUse;
    /* list of Tx buffers ready for data (under MINIPORT management) */
    LIST_ENTRY              NetFreeSendBuffers;
    /* current number of free Tx descriptors */
    UINT                    nofFreeTxDescriptors;
    /* initial number of free Tx descriptor(from cfg) - max number of available Tx descriptors */
    UINT                    maxFreeTxDescriptors;
    /* current number of free Tx buffers, which can be submitted */
    UINT                    nofFreeHardwareBuffers;
    /* maximal number of free Tx buffers, which can be used by SG */
    UINT                    maxFreeHardwareBuffers;
    /* minimal number of free Tx buffers */
    UINT                    minFreeHardwareBuffers;
    /* current number of Tx packets (or lists) to return */
    LONG                    NetTxPacketsToReturn;
    /* total of Rx buffer in turnaround */
    UINT                    NetMaxReceiveBuffers;
    struct VirtIOBufferDescriptor *sgTxGatherTable;
    UINT                    nPnpEventIndex;
    NDIS_DEVICE_PNP_EVENT   PnpEvents[16];
    tOffloadSettings        Offload;
    NDIS_OFFLOAD_PARAMETERS InitialOffloadParameters;
    // we keep these members common for XP and Vista
    // for XP and non-MSI case of Vista they are set to zero
    ULONG                       ulRxMessage;
    ULONG                       ulTxMessage;
    ULONG                       ulControlMessage;

    NDIS_MINIPORT_INTERRUPT     Interrupt;
    NDIS_HANDLE                 PacketPool;
    NDIS_HANDLE                 BuffersPool;
    NDIS_HANDLE                 WrapperConfigurationHandle;
    LIST_ENTRY                  SendQueue;
    LIST_ENTRY                  TxWaitingList;
    NDIS_EVENT                  HaltEvent;
    NDIS_TIMER                  ConnectTimer;
    NDIS_TIMER                  DPCPostProcessTimer;
    BOOLEAN                     bDmaInitialized;
}PARANDIS_ADAPTER, *PPARANDIS_ADAPTER;

typedef enum { cpeOK, cpeNoBuffer, cpeInternalError, cpeTooLarge, cpeNoIndirect } tCopyPacketError;
typedef struct _tagCopyPacketResult
{
    ULONG       size;
    tCopyPacketError error;
}tCopyPacketResult;

typedef struct _tagSynchronizedContext
{
    PARANDIS_ADAPTER    *pContext;
    PVOID               Parameter;
}tSynchronizedContext;

typedef BOOLEAN (*tSynchronizedProcedure)(tSynchronizedContext *context);

/**********************************************************
LAZY release procedure returns buffers to VirtIO
only where there are no free buffers available

NON-LAZY release releases transmit buffers from VirtIO
library every time there is something to release
***********************************************************/
//#define LAZY_TX_RELEASE

static inline bool VirtIODeviceGetHostFeature(PARANDIS_ADAPTER *pContext, unsigned uFeature)
{
   DPrintf(4, ("%s\n", __FUNCTION__));

   return virtio_is_feature_enabled(pContext->ullHostFeatures, uFeature);
}

static inline void VirtIODeviceEnableGuestFeature(PARANDIS_ADAPTER *pContext, unsigned uFeature)
{
   ULONG ulValue = 0;
   DPrintf(4, ("%s\n", __FUNCTION__));

   virtio_feature_enable(pContext->ullGuestFeatures, uFeature);
}

BOOLEAN FORCEINLINE IsTimeToReleaseTx(PARANDIS_ADAPTER *pContext)
{
#ifndef LAZY_TX_RELEASE
    return pContext->nofFreeTxDescriptors < pContext->maxFreeTxDescriptors;
#else
    return pContext->nofFreeTxDescriptors == 0;
#endif
}

BOOLEAN FORCEINLINE IsValidVlanId(PARANDIS_ADAPTER *pContext, ULONG VlanID)
{
    return pContext->VlanId == 0 || pContext->VlanId == VlanID;
}

BOOLEAN FORCEINLINE IsVlanSupported(PARANDIS_ADAPTER *pContext)
{
    return pContext->ulPriorityVlanSetting & 2;
}

BOOLEAN FORCEINLINE IsPrioritySupported(PARANDIS_ADAPTER *pContext)
{
    return pContext->ulPriorityVlanSetting & 1;
}

BOOLEAN ParaNdis_ValidateMacAddress(
    PUCHAR pcMacAddress,
    BOOLEAN bLocal);

NDIS_STATUS ParaNdis_InitializeContext(
    PARANDIS_ADAPTER *pContext,
    PNDIS_RESOURCE_LIST ResourceList);

NDIS_STATUS ParaNdis_FinishInitialization(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_CleanupContext(
    PARANDIS_ADAPTER *pContext);


UINT ParaNdis_VirtIONetReleaseTransmitBuffers(
    PARANDIS_ADAPTER *pContext);

ULONG ParaNdis_DPCWorkBody(
  PARANDIS_ADAPTER *pContext,
  ULONG ulMaxPacketsToIndicate);

NDIS_STATUS ParaNdis_SetMulticastList(
    PARANDIS_ADAPTER *pContext,
    PVOID Buffer,
    ULONG BufferSize,
    PUINT pBytesRead,
    PUINT pBytesNeeded);

VOID ParaNdis_VirtIOEnableIrqSynchronized(
    PARANDIS_ADAPTER *pContext,
    ULONG interruptSource);

VOID ParaNdis_VirtIODisableIrqSynchronized(
    PARANDIS_ADAPTER *pContext,
    ULONG interruptSource);

static __inline struct virtqueue *
ParaNdis_GetQueueForInterrupt(PARANDIS_ADAPTER *pContext, ULONG interruptSource)
{
    if (interruptSource & isTransmit)
        return pContext->NetSendQueue;
    if (interruptSource & isReceive)
        return pContext->NetReceiveQueue;

    return NULL;
}

static __inline BOOLEAN
ParaNDIS_IsQueueInterruptEnabled(struct virtqueue * _vq)
{
    return virtqueue_is_interrupt_enabled(_vq);
}

VOID ParaNdis_OnPnPEvent(
    PARANDIS_ADAPTER *pContext,
    NDIS_DEVICE_PNP_EVENT pEvent,
    PVOID   pInfo,
    ULONG   ulSize);

BOOLEAN ParaNdis_OnLegacyInterrupt(
    PARANDIS_ADAPTER *pContext,
    BOOLEAN *pRunDpc);

BOOLEAN ParaNdis_OnQueuedInterrupt(
    PARANDIS_ADAPTER *pContext,
    BOOLEAN *pRunDpc,
    ULONG knownInterruptSources);

VOID ParaNdis_OnShutdown(
    PARANDIS_ADAPTER *pContext);

BOOLEAN ParaNdis_CheckForHang(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_ReportLinkStatus(
    PARANDIS_ADAPTER *pContext,
    BOOLEAN bForce);

NDIS_STATUS ParaNdis_PowerOn(
    PARANDIS_ADAPTER *pContext
);

VOID ParaNdis_PowerOff(
    PARANDIS_ADAPTER *pContext
);

void ParaNdis_DebugInitialize(PVOID DriverObject,PVOID RegistryPath);
void ParaNdis_DebugCleanup(PDRIVER_OBJECT  pDriverObject);
void ParaNdis_DebugRegisterMiniport(PARANDIS_ADAPTER *pContext, BOOLEAN bRegister);


//#define ENABLE_HISTORY_LOG
#if !defined(ENABLE_HISTORY_LOG)

void FORCEINLINE ParaNdis_DebugHistory(
    PARANDIS_ADAPTER *pContext,
    eHistoryLogOperation op,
    PVOID pParam1,
    ULONG lParam2,
    ULONG lParam3,
    ULONG lParam4)
{

}

#else

void ParaNdis_DebugHistory(
    PARANDIS_ADAPTER *pContext,
    eHistoryLogOperation op,
    PVOID pParam1,
    ULONG lParam2,
    ULONG lParam3,
    ULONG lParam4);

#endif

typedef struct _tagTxOperationParameters
{
    tPacketType     packet;
    PVOID           ReferenceValue;
    UINT            nofSGFragments;
    ULONG           ulDataSize;
    ULONG           offloadMss;
    ULONG           tcpHeaderOffset;
    ULONG           flags;      //see tPacketOffloadRequest
}tTxOperationParameters;

tCopyPacketResult ParaNdis_DoCopyPacketData(
    PARANDIS_ADAPTER *pContext,
    tTxOperationParameters *pParams);

typedef struct _tagMapperResult
{
    USHORT  usBuffersMapped;
    USHORT  usBufferSpaceUsed;
    ULONG   ulDataSize;
}tMapperResult;


tCopyPacketResult ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER *pContext, tTxOperationParameters *Params);

void ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER *pContext, tOffloadSettingsFlags *pDest, PULONG from);

tChecksumCheckResult ParaNdis_CheckRxChecksum(PARANDIS_ADAPTER *pContext, ULONG virtioFlags, PVOID pRxPacket, ULONG len);

void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext);

/*****************************************************
Procedures to implement for NDIS specific implementation
******************************************************/

PVOID ParaNdis_AllocateMemory(
    PARANDIS_ADAPTER *pContext,
    ULONG ulRequiredSize);

NDIS_STATUS ParaNdis_FinishSpecificInitialization(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_FinalizeCleanup(
    PARANDIS_ADAPTER *pContext);

NDIS_HANDLE ParaNdis_OpenNICConfiguration(
    PARANDIS_ADAPTER *pContext);

tPacketIndicationType ParaNdis_IndicateReceivedPacket(
    PARANDIS_ADAPTER *pContext,
    PVOID dataBuffer,
    PULONG pLength,
    BOOLEAN bPrepareOnly,
    pIONetDescriptor pBufferDesc);

VOID ParaNdis_IndicateReceivedBatch(
    PARANDIS_ADAPTER *pContext,
    tPacketIndicationType *pBatch,
    ULONG nofPackets);

VOID ParaNdis_PacketMapper(
    PARANDIS_ADAPTER *pContext,
    tPacketType packet,
    PVOID Reference,
    struct VirtIOBufferDescriptor *buffers,
    pIONetDescriptor pDesc,
    tMapperResult *pMapperResult
    );

tCopyPacketResult ParaNdis_PacketCopier(
    tPacketType packet,
    PVOID dest,
    ULONG maxSize,
    PVOID refValue,
    BOOLEAN bPreview);

BOOLEAN ParaNdis_ProcessTx(
    PARANDIS_ADAPTER *pContext,
    BOOLEAN IsDpc,
    BOOLEAN IsInterrupt);

BOOLEAN ParaNdis_SetTimer(
    NDIS_HANDLE timer,
    LONG millies);

BOOLEAN ParaNdis_SynchronizeWithInterrupt(
    PARANDIS_ADAPTER *pContext,
    ULONG messageId,
    tSynchronizedProcedure procedure,
    PVOID parameter);

VOID ParaNdis_Suspend(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_Resume(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_OnTransmitBufferReleased(
    PARANDIS_ADAPTER *pContext,
    IONetDescriptor *pDesc);


typedef VOID (*tOnAdditionalPhysicalMemoryAllocated)(
    PARANDIS_ADAPTER *pContext,
    tCompletePhysicalAddress *pAddresses);


typedef struct _tagPhysicalAddressAllocationContext
{
    tCompletePhysicalAddress address;
    PARANDIS_ADAPTER *pContext;
    tOnAdditionalPhysicalMemoryAllocated Callback;
} tPhysicalAddressAllocationContext;


BOOLEAN ParaNdis_InitialAllocatePhysicalMemory(
    PARANDIS_ADAPTER *pContext,
    tCompletePhysicalAddress *pAddresses);

VOID ParaNdis_FreePhysicalMemory(
    PARANDIS_ADAPTER *pContext,
    tCompletePhysicalAddress *pAddresses);

BOOLEAN ParaNdis_BindBufferToPacket(
    PARANDIS_ADAPTER *pContext,
    pIONetDescriptor pBufferDesc);

void ParaNdis_UnbindBufferFromPacket(
    PARANDIS_ADAPTER *pContext,
    pIONetDescriptor pBufferDesc);

void ParaNdis_IndicateConnect(
    PARANDIS_ADAPTER *pContext,
    BOOLEAN bConnected,
    BOOLEAN bForce);

void ParaNdis_RestoreDeviceConfigurationAfterReset(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_UpdateDeviceFilters(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_DeviceFiltersUpdateVlanId(
    PARANDIS_ADAPTER *pContext);

VOID ParaNdis_SetPowerState(
    PARANDIS_ADAPTER *pContext,
    NDIS_DEVICE_POWER_STATE newState);


#endif //-OFFLOAD_UNIT_TEST

typedef enum _tagppResult
{
    ppresNotTested = 0,
    ppresNotIP     = 1,
    ppresIPV4      = 2,
    ppresIPV6      = 3,
    ppresIPTooShort  = 1,
    ppresPCSOK       = 1,
    ppresCSOK        = 2,
    ppresCSBad       = 3,
    ppresXxpOther    = 1,
    ppresXxpKnown    = 2,
    ppresXxpIncomplete = 3,
    ppresIsTCP         = 0,
    ppresIsUDP         = 1,
}ppResult;

typedef union _tagTcpIpPacketParsingResult
{
    struct {
        /* 0 - not tested, 1 - not IP, 2 - IPV4, 3 - IPV6 */
        ULONG ipStatus          : 2;
        /* 0 - not tested, 1 - n/a, 2 - CS, 3 - bad */
        ULONG ipCheckSum        : 2;
        /* 0 - not tested, 1 - PCS, 2 - CS, 3 - bad */
        ULONG xxpCheckSum       : 2;
        /* 0 - not tested, 1 - other, 2 - known(contains basic TCP or UDP header), 3 - known incomplete */
        ULONG xxpStatus         : 2;
        /* 1 - contains complete payload */
        ULONG xxpFull           : 1;
        ULONG TcpUdp            : 1;
        ULONG fixedIpCS         : 1;
        ULONG fixedXxpCS        : 1;
        ULONG IsFragment        : 1;
        ULONG reserved          : 3;
        ULONG ipHeaderSize      : 8;
        ULONG XxpIpHeaderSize   : 8;
    };
    ULONG value;
}tTcpIpPacketParsingResult;

typedef enum _tagPacketOffloadRequest
{
    pcrIpChecksum  = (1 << 0),
    pcrTcpV4Checksum = (1 << 1),
    pcrUdpV4Checksum = (1 << 2),
    pcrTcpV6Checksum = (1 << 3),
    pcrUdpV6Checksum = (1 << 4),
    pcrTcpChecksum = (pcrTcpV4Checksum | pcrTcpV6Checksum),
    pcrUdpChecksum = (pcrUdpV4Checksum | pcrUdpV6Checksum),
    pcrAnyChecksum = (pcrIpChecksum | pcrTcpV4Checksum | pcrUdpV4Checksum | pcrTcpV6Checksum | pcrUdpV6Checksum),
    pcrLSO   = (1 << 5),
    pcrIsIP  = (1 << 6),
    pcrFixIPChecksum = (1 << 7),
    pcrFixPHChecksum = (1 << 8),
    pcrFixTcpV4Checksum = (1 << 9),
    pcrFixUdpV4Checksum = (1 << 10),
    pcrFixTcpV6Checksum = (1 << 11),
    pcrFixUdpV6Checksum = (1 << 12),
    pcrFixXxpChecksum = (pcrFixTcpV4Checksum | pcrFixUdpV4Checksum | pcrFixTcpV6Checksum | pcrFixUdpV6Checksum),
    pcrPriorityTag = (1 << 13),
    pcrNoIndirect  = (1 << 14)
}tPacketOffloadRequest;

// sw offload
tTcpIpPacketParsingResult ParaNdis_CheckSumVerify(PVOID buffer, ULONG size, ULONG flags, LPCSTR caller);
tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, LPCSTR caller);

void ParaNdis_PadPacketReceived(PVOID pDataBuffer, PULONG pLength);

#endif
