/*************************************************************
Required NDIS procedure
Stops TX and RX path and finished the function of adapter
*************************************************************/
static VOID ParaNdis5_Halt(
	IN NDIS_HANDLE MiniportAdapterContext)
{
	NDIS_STATUS status = NDIS_STATUS_SUCCESS;
	BOOLEAN bUnused;
	PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
	DEBUG_ENTRY(0);

	ParaNdis_DebugHistory(pContext, hopHalt, NULL, 1, 0, 0);

	NdisCancelTimer(&pContext->ConnectTimer, &bUnused);
	NdisResetEvent(&pContext->HaltEvent);
	if (NDIS_STATUS_PENDING != ParaNdis5_StopSend(pContext, TRUE, OnSendStopped))
		NdisSetEvent(&pContext->HaltEvent);
	WaitHaltEvent(pContext, "Send");
	NdisResetEvent(&pContext->HaltEvent);
	if (NDIS_STATUS_PENDING != ParaNdis5_StopReceive(pContext, TRUE, OnReceiveStopped))
		NdisSetEvent(&pContext->HaltEvent);
	WaitHaltEvent(pContext, "Receive");
	ParaNdis_CleanupContext(pContext);
	NdisCancelTimer(&pContext->DPCPostProcessTimer, &bUnused);
	ParaNdis_DebugHistory(pContext, hopHalt, NULL, 0, 0, 0);
	ParaNdis_DebugRegisterMiniport(pContext, FALSE);
	NdisFreeMemory(pContext, 0, 0);
	DEBUG_EXIT_STATUS(0, status);
}
/**********************************************************
Required NDIS handler for RESET operation
Never happens under normal condition, only if
OID or other call returns PENDING and not completed or if
ParaNdis6_CheckForHang returns true
***********************************************************/
static NDIS_STATUS ParaNdis6_Reset(
        NDIS_HANDLE miniportAdapterContext,
        PBOOLEAN  pAddressingReset)
{
    NDIS_STATUS  status = NDIS_STATUS_FAILURE;
    PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext;
    NDIS_HANDLE hwo;
    tGeneralWorkItem *pwi;
    DEBUG_ENTRY(0);
    *pAddressingReset = TRUE;
    ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 1, 0, 0);
    hwo = NdisAllocateIoWorkItem(pContext->MiniportHandle);
    pwi = ParaNdis_AllocateMemory(pContext, sizeof(tGeneralWorkItem));
    if (pwi && hwo)
    {
        pwi->pContext = pContext;
        pwi->WorkItem = hwo;
        NdisQueueIoWorkItem(hwo, OnResetWorkItem, pwi);
        status = NDIS_STATUS_PENDING;
    }
    else
    {
        if (pwi) NdisFreeMemory(pwi, 0, 0);
        if (hwo) NdisFreeIoWorkItem(hwo);
        ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 0, status, 0);
    }
    DEBUG_EXIT_STATUS(0, status);
    return status;
}
/**********************************************************
Deregister driver
Clean up WPP
***********************************************************/
static VOID ParaNdis6_Unload(IN PDRIVER_OBJECT pDriverObject)
{
    DEBUG_ENTRY(0);
    if (DriverHandle) NdisMDeregisterMiniportDriver(DriverHandle);
    DEBUG_EXIT_STATUS(2, 0);
    ParaNdis_DebugCleanup(pDriverObject);
}
/**********************************************************
called at IRQL = PASSIVE_LEVEL
Called on disable, on removal, on standby (if required)
***********************************************************/
static VOID ParaNdis6_Halt(NDIS_HANDLE miniportAdapterContext, NDIS_HALT_ACTION haltAction)
{
    PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext;
    DEBUG_ENTRY(0);
    ParaNdis_DebugHistory(pContext, hopHalt, NULL, 1, haltAction, 0);
    ParaNdis_CleanupContext(pContext);
    ParaNdis_DebugHistory(pContext, hopHalt, NULL, 0, 0, 0);
    ParaNdis_DebugRegisterMiniport(pContext, FALSE);
    NdisFreeMemory(pContext, 0, 0);
    DEBUG_EXIT_STATUS(2, 0);
}
/*************************************************************
Required NDIS procedure
Responsible for hardware interrupt handling
*************************************************************/
static VOID ParaNdis5_MiniportISR(OUT PBOOLEAN InterruptRecognized,
							   OUT PBOOLEAN QueueMiniportHandleInterrupt,
							   IN NDIS_HANDLE  MiniportAdapterContext)
{
	PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
	BOOLEAN b;
	*QueueMiniportHandleInterrupt = FALSE;
	b = ParaNdis_OnLegacyInterrupt(pContext, QueueMiniportHandleInterrupt);
	*InterruptRecognized = b;
	DEBUG_EXIT_STATUS(7, (ULONG)b);
}
/*
  We enter this procedure in two cases:
  - number of MSIX vectors is at least nPathBundles*2 + 1
  - there is single MSIX vector and one path bundle
*/
NDIS_STATUS ParaNdis_ConfigureMSIXVectors(PARANDIS_ADAPTER *pContext)
{
    NDIS_STATUS status = NDIS_STATUS_RESOURCES;
    UINT i;
    PIO_INTERRUPT_MESSAGE_INFO pTable = pContext->pMSIXInfoTable;
    bool bSingleVector = pContext->pMSIXInfoTable->MessageCount == 1;
    if (pTable && pTable->MessageCount)
    {
        status = NDIS_STATUS_SUCCESS;
        DPrintf(0, ("[%s] Using MSIX interrupts (%d messages, irql %d)\n",
            __FUNCTION__, pTable->MessageCount, pTable->UnifiedIrql));
        for (i = 0; i < pContext->pMSIXInfoTable->MessageCount; ++i)
        {
            DPrintf(0, ("[%s] MSIX message%d=%08X=>%I64X\n",
                __FUNCTION__, i,
                pTable->MessageInfo[i].MessageData,
                pTable->MessageInfo[i].MessageAddress));
        }
        for (UINT j = 0; j < pContext->nPathBundles && status == NDIS_STATUS_SUCCESS; ++j)
        {
            u16 vector = 2 * u16(j);
            status = pContext->pPathBundles[j].txPath.SetupMessageIndex(vector);
            if (status == NDIS_STATUS_SUCCESS)
            {
                if (!bSingleVector) vector++;
                status = pContext->pPathBundles[j].rxPath.SetupMessageIndex(vector);
            }
            DPrintf(0, ("[%s] Using messages %u/%u for RX/TX queue %u\n", __FUNCTION__,
                        pContext->pPathBundles[j].rxPath.getMessageIndex(),
                        pContext->pPathBundles[j].txPath.getMessageIndex(),
                        j));
        }

        if (status == NDIS_STATUS_SUCCESS && pContext->bCXPathCreated)
        {
            /*
            Usually there is own vector for control queue.
            In corner case of single vector control queue uses the same vector as RX and TX
            */
            if (bSingleVector)
            {
                status = pContext->CXPath.SetupMessageIndex(0);
            }
            else
            {
                status = pContext->CXPath.SetupMessageIndex(2 * u16(pContext->nPathBundles));
            }
        }
    }

    DEBUG_EXIT_STATUS(0, status);
    return status;
}
NDIS_STATUS ParaNdis_ConfigureMSIXVectors(PARANDIS_ADAPTER *pContext)
{
    NDIS_STATUS status = NDIS_STATUS_RESOURCES;
    UINT i;
    PIO_INTERRUPT_MESSAGE_INFO pTable = pContext->pMSIXInfoTable;
    if (pTable && pTable->MessageCount)
    {
        status = NDIS_STATUS_SUCCESS;
        DPrintf(0, ("[%s] Using MSIX interrupts (%d messages, irql %d)\n",
            __FUNCTION__, pTable->MessageCount, pTable->UnifiedIrql));
        for (i = 0; i < pContext->pMSIXInfoTable->MessageCount; ++i)
        {
            DPrintf(0, ("[%s] MSIX message%d=%08X=>%I64X\n",
                __FUNCTION__, i,
                pTable->MessageInfo[i].MessageData,
                pTable->MessageInfo[i].MessageAddress));
        }
        for (UINT j = 0; j < pContext->nPathBundles && status == NDIS_STATUS_SUCCESS; ++j)
        {
            status = pContext->pPathBundles[j].rxPath.SetupMessageIndex(2 * u16(j) + 1);
            status = pContext->pPathBundles[j].txPath.SetupMessageIndex(2 * u16(j));
        }

        if (pContext->bCXPathCreated)
        {
            pContext->CXPath.SetupMessageIndex(2 * u16(pContext->nPathBundles));
        }
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        for (UINT j = 0; j < pContext->nPathBundles && status == NDIS_STATUS_SUCCESS; ++j)
        {
            DPrintf(0, ("[%s] Using messages %u/%u for RX/TX queue %u\n", __FUNCTION__, pContext->pPathBundles[j].rxPath.getMessageIndex(),
                pContext->pPathBundles[j].txPath.getMessageIndex(), j));
        }
        if (pContext->bCXPathCreated)
        {
            DPrintf(0, ("[%s] Using message %u for controls\n", __FUNCTION__, pContext->CXPath.getMessageIndex()));
        }
        else
        {
            DPrintf(0, ("[%s] - No control path\n", __FUNCTION__));
        }
    }
    DEBUG_EXIT_STATUS(2, status);
    return status;
}
/**********************************************************
Implements opening of adapter-specific configuration
Parameters:

Return value:
    NDIS_HANDLE Handle of open configuration
    NULL on error
***********************************************************/
NDIS_HANDLE ParaNdis_OpenNICConfiguration(PARANDIS_ADAPTER *pContext)
{
    NDIS_CONFIGURATION_OBJECT co;
    NDIS_HANDLE cfg;
    NDIS_STATUS status;
    DEBUG_ENTRY(2);
    co.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
    co.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
    co.Header.Size = sizeof(co);
    co.Flags = 0;
    co.NdisHandle = pContext->MiniportHandle;
    status = NdisOpenConfigurationEx(&co, &cfg);
    if (status != NDIS_STATUS_SUCCESS)
        cfg = NULL;
    DEBUG_EXIT_STATUS(status == NDIS_STATUS_SUCCESS ? 2 : 0, status);
    return cfg;
}
/**********************************************************
Required NDIS handler
called at IRQL = PASSIVE_LEVEL
Must pause RX and TX
Called upon startup, on resume,
upon protocols activation
***********************************************************/
static NDIS_STATUS ParaNdis6_Restart(
    NDIS_HANDLE                         miniportAdapterContext,
    PNDIS_MINIPORT_RESTART_PARAMETERS   miniportRestartParameters)
{
    NDIS_STATUS  status = NDIS_STATUS_SUCCESS;
    PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext;
    DEBUG_ENTRY(0);

    UNREFERENCED_PARAMETER(miniportRestartParameters);

    ParaNdis_DebugHistory(pContext, hopSysResume, NULL, 1, 0, 0);
    ParaNdis6_SendPauseRestart(pContext, FALSE, NULL);
    ParaNdis6_ReceivePauseRestart(pContext, FALSE, NULL);

    ParaNdis_DebugHistory(pContext, hopSysResume, NULL, 0, 0, 0);
    DEBUG_EXIT_STATUS(2, status);
    return status;
}
VOID ParaNdis_Suspend(PARANDIS_ADAPTER *pContext)
{
	DEBUG_ENTRY(0);
	NdisResetEvent(&pContext->ResetEvent);
	if (NDIS_STATUS_PENDING != ParaNdis5_StopSend(pContext, TRUE, OnSendStoppedOnReset))
	{
		NdisSetEvent(&pContext->ResetEvent);
	}
	NdisWaitEvent(&pContext->ResetEvent, 0);
	NdisResetEvent(&pContext->ResetEvent);
	if (NDIS_STATUS_PENDING != ParaNdis5_StopReceive(pContext, TRUE, OnReceiveStoppedOnReset))
	{
		NdisSetEvent(&pContext->ResetEvent);
	}
	NdisWaitEvent(&pContext->ResetEvent, 0);
	NdisResetEvent(&pContext->ResetEvent);
	DEBUG_EXIT_STATUS(0, 0);
}
/**********************************************************
Required NDIS handler
called at IRQL = PASSIVE_LEVEL
Must pause RX and TX
Called before halt, on standby,
upon protocols activation
***********************************************************/
static NDIS_STATUS ParaNdis6_Pause(
        NDIS_HANDLE                         miniportAdapterContext,
        PNDIS_MINIPORT_PAUSE_PARAMETERS     miniportPauseParameters)
{
    NDIS_STATUS  status;
    PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext;
    DEBUG_ENTRY(0);

    UNREFERENCED_PARAMETER(miniportPauseParameters);

    ParaNdis_DebugHistory(pContext, hopSysPause, NULL, 1, 0, 0);
    status = ParaNdis6_SendPauseRestart(pContext, TRUE, OnSendPauseComplete);
    if (status != NDIS_STATUS_PENDING)
    {
        status = ParaNdis6_ReceivePauseRestart(pContext, TRUE, OnReceivePauseComplete);
    }
    DEBUG_EXIT_STATUS(0, status);
    if (status == STATUS_SUCCESS)
    {
        // pause exit
        ParaNdis_DebugHistory(pContext, hopSysPause, NULL, 0, 0, 0);
    }
    return status;
}
VOID ParaNdis_Suspend(PARANDIS_ADAPTER *pContext)
{
    DPrintf(0, ("[%s]%s\n", __FUNCTION__, pContext->bFastSuspendInProcess ? "(Fast)" : ""));
    NdisResetEvent(&pContext->ResetEvent);
    if (NDIS_STATUS_PENDING != ParaNdis6_SendPauseRestart(pContext, TRUE, OnSendPauseCompleteOnReset))
    {
        NdisSetEvent(&pContext->ResetEvent);
    }
    NdisWaitEvent(&pContext->ResetEvent, 0);
    if (!pContext->bFastSuspendInProcess)
    {
        NdisResetEvent(&pContext->ResetEvent);
        if (NDIS_STATUS_PENDING != ParaNdis6_ReceivePauseRestart(pContext, TRUE, OnReceivePauseCompleteOnReset))
        {
            NdisSetEvent(&pContext->ResetEvent);
        }
        NdisWaitEvent(&pContext->ResetEvent, 0);
    }
    else
    {
        ParaNdis6_ReceivePauseRestart(pContext, TRUE, NULL);
    }
    DEBUG_EXIT_STATUS(0, 0);
}
/******************************************************
Required NDIS procedure
Allocates and initializes adapter context
Finally sets send and receive to Enabled state and reports connect
Returns:
NDIS_STATUS	SUCCESS or some error code
*******************************************************/
static NDIS_STATUS ParaNdis5_Initialize(OUT PNDIS_STATUS OpenErrorStatus,
									OUT PUINT SelectedMediumIndex,
									IN PNDIS_MEDIUM MediumArray,
									IN UINT MediumArraySize,
									IN NDIS_HANDLE MiniportAdapterHandle,
									IN NDIS_HANDLE WrapperConfigurationContext)
{
	NDIS_STATUS  status = NDIS_STATUS_UNSUPPORTED_MEDIA;
	PARANDIS_ADAPTER *pContext = NULL;
	UINT i;
	for(i = 0; i < MediumArraySize; ++i)
	{
		if(MediumArray[i] == NdisMedium802_3)
		{
			*SelectedMediumIndex = i;
			status = NDIS_STATUS_SUCCESS;
			break;
		}
	}

	if (status == NDIS_STATUS_SUCCESS)
	{
		pContext =
			(PARANDIS_ADAPTER *)ParaNdis_AllocateMemory(NULL, sizeof(PARANDIS_ADAPTER));
		if (!pContext)
		{
			status = NDIS_STATUS_RESOURCES;
		}
	}

	if (status == NDIS_STATUS_SUCCESS)
	{
		PVOID pResourceList = &status;
		UINT  uSize = 0;
		NdisZeroMemory(pContext, sizeof(PARANDIS_ADAPTER));
		pContext->ulUniqueID = InterlockedIncrement(&gID);
		pContext->DriverHandle = DriverHandle;
		pContext->MiniportHandle = MiniportAdapterHandle;
		pContext->WrapperConfigurationHandle = WrapperConfigurationContext;
		NdisMQueryAdapterResources(&status, WrapperConfigurationContext, pResourceList, &uSize);
		if (uSize > 0)
			pResourceList = ParaNdis_AllocateMemory(MiniportAdapterHandle, uSize);
		else
			pResourceList = NULL;
		if (!pResourceList)
			status = uSize > 0 ? NDIS_STATUS_RESOURCES : NDIS_STATUS_FAILURE;
		else
		{
			ULONG attributes;
			attributes = NDIS_ATTRIBUTE_DESERIALIZE | NDIS_ATTRIBUTE_BUS_MASTER;
			// in XP SP2, if this flag is NOT set, the NDIS halts miniport
			// upon transition to S1..S4.
			// it seems that XP SP3 ignores it and always sends SET_POWER to D3
#ifndef NO_XP_POWER_MANAGEMENT
			attributes |= NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND;
#endif
#ifdef NDIS50_MINIPORT
			//TODO: this is wrong, I think
			// this API is for XP and
			// it should be used only if you never need virtual addresses of sent buffers
			// so I comment it out
			//attributes |= NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS;
#endif
			NdisMSetAttributesEx(
				MiniportAdapterHandle,
				pContext,
				0,
				attributes,
				NdisInterfacePci);
			NdisMQueryAdapterResources(&status, WrapperConfigurationContext, pResourceList, &uSize);
			status = ParaNdis_InitializeContext(pContext, (PNDIS_RESOURCE_LIST)pResourceList);
			NdisFreeMemory(pResourceList, 0, 0);
		}
	}

	if (status == NDIS_STATUS_SUCCESS)
	{
		status = ParaNdis_FinishInitialization(pContext);
		if (status == NDIS_STATUS_SUCCESS)
		{
#ifdef NDIS50_MINIPORT
			NdisMRegisterAdapterShutdownHandler(
				MiniportAdapterHandle,
				pContext,
				(ADAPTER_SHUTDOWN_HANDLER)ParaNdis5_Shutdown);
#endif //NDIS50_MINIPORT
			ParaNdis_DebugRegisterMiniport(pContext, TRUE);
			ParaNdis_IndicateConnect(pContext, FALSE, TRUE);
			ParaNdis5_StopSend(pContext, FALSE, NULL);
			ParaNdis5_StopReceive(pContext, FALSE, NULL);
			if (!pContext->ulMilliesToConnect)
			{
				ParaNdis_ReportLinkStatus(pContext, FALSE);
			}
			else
			{
				NdisSetTimer(&pContext->ConnectTimer, pContext->ulMilliesToConnect);
			}
		}
		else
		{
			ParaNdis_CleanupContext(pContext);
		}
	}

	if (status != NDIS_STATUS_SUCCESS && pContext)
	{
		NdisFreeMemory(pContext, 0, 0);
	}

	DEBUG_EXIT_STATUS(0, status);
	return status;
}
/*************************************************************
Driver's entry point
Parameters:
	as usual
Return value:
	SUCCESS or error code
*************************************************************/
NDIS_STATUS DriverEntry(PVOID DriverObject,PVOID RegistryPath)
{
	NDIS_STATUS status;
	NDIS_MINIPORT_CHARACTERISTICS chars;
	ParaNdis_DebugInitialize(DriverObject, RegistryPath);

	status = NDIS_STATUS_FAILURE;

	DEBUG_ENTRY(0);
	_LogOutString(0, __DATE__ " " __TIME__);

	NdisMInitializeWrapper(&DriverHandle,
						   DriverObject,
						   RegistryPath,
						   NULL
						   );

	if (DriverHandle)
	{
		NdisZeroMemory(&chars, sizeof(chars));
		//NDIS version of the miniport
		chars.MajorNdisVersion			= NDIS_MINIPORT_MAJOR_VERSION;
		chars.MinorNdisVersion			= NDIS_MINIPORT_MINOR_VERSION;
		//Init and destruction
		chars.InitializeHandler			= ParaNdis5_Initialize;
		chars.HaltHandler				= ParaNdis5_Halt;

		//Interrupt and DPC handling
		chars.HandleInterruptHandler	= ParaNdis5_HandleDPC;
		chars.ISRHandler				= ParaNdis5_MiniportISR;

		//Packet transfer - send path and notification on the send packet
		chars.SendPacketsHandler		= ParaNdis5_SendPackets;
		chars.ReturnPacketHandler		= ParaNdis5_ReturnPacket;

		//OID set\get
		chars.SetInformationHandler		= ParaNdis5_SetOID;
		chars.QueryInformationHandler	= ParaNdis5_QueryOID;

		//Reset
		chars.ResetHandler				= ParaNdis5_Reset;
		chars.CheckForHangHandler		= ParaNdis5_CheckForHang; //optional

#ifdef NDIS51_MINIPORT
		chars.CancelSendPacketsHandler	= ParaNdis5_CancelSendPackets;
		chars.PnPEventNotifyHandler		= ParaNdis5_PnPEventNotify;
		chars.AdapterShutdownHandler	= ParaNdis5_Shutdown;
#endif
		status = NdisMRegisterMiniport(
			DriverHandle,
			&chars,
			sizeof(chars));
	}

	if (status == NDIS_STATUS_SUCCESS)
	{
		NdisMRegisterUnloadHandler(DriverHandle, ParaVirtualNICUnload);
	}
	else if (DriverHandle)
	{
		DPrintf(0, ("NdisMRegisterMiniport failed"));
		NdisTerminateWrapper(DriverHandle, NULL);
	}
	else
	{
		DPrintf(0, ("NdisMInitializeWrapper failed"));
	}

	DEBUG_EXIT_STATUS(status ? 0 : 4, status);
	return status;
}
/**********************************************************
NDIS6-related final initialization:
    Installing interrupt handler
    Allocate buffer list pool

Parameters:

Return value:

***********************************************************/
NDIS_STATUS ParaNdis_FinishSpecificInitialization(PARANDIS_ADAPTER *pContext)
{
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
    NET_BUFFER_LIST_POOL_PARAMETERS PoolParams;
    NDIS_MINIPORT_INTERRUPT_CHARACTERISTICS mic;
    DEBUG_ENTRY(0);

    NdisZeroMemory(&mic, sizeof(mic));
    mic.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_INTERRUPT;
    mic.Header.Revision = NDIS_MINIPORT_INTERRUPT_REVISION_1;
    mic.Header.Size = NDIS_SIZEOF_MINIPORT_INTERRUPT_CHARACTERISTICS_REVISION_1;
    mic.DisableInterruptHandler = MiniportDisableInterruptEx;
    mic.EnableInterruptHandler  = MiniportEnableInterruptEx;
    mic.InterruptDpcHandler = MiniportInterruptDPC;
    mic.InterruptHandler = MiniportInterrupt;
    if (pContext->bUsingMSIX)
    {
        mic.MsiSupported = TRUE;
        mic.MsiSyncWithAllMessages = TRUE;
        mic.EnableMessageInterruptHandler = MiniportEnableMSIInterrupt;
        mic.DisableMessageInterruptHandler = MiniportDisableMSIInterrupt;
        mic.MessageInterruptHandler = MiniportMSIInterrupt;
        mic.MessageInterruptDpcHandler = MiniportMSIInterruptDpc;
    }
    PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
    PoolParams.Header.Size = sizeof(PoolParams);
    PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
    PoolParams.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;
    PoolParams.fAllocateNetBuffer = TRUE;
    PoolParams.ContextSize = 0;
    PoolParams.PoolTag = PARANDIS_MEMORY_TAG;
    PoolParams.DataSize = 0;

    pContext->BufferListsPool = NdisAllocateNetBufferListPool(pContext->MiniportHandle, &PoolParams);
    if (!pContext->BufferListsPool)
    {
        status = NDIS_STATUS_RESOURCES;
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        status = NdisMRegisterInterruptEx(pContext->MiniportHandle, pContext, &mic, &pContext->InterruptHandle);
    }

    if (pContext->bUsingMSIX)
    {
        DPrintf(0, ("[%s] MSIX message table %savailable, count = %u\n", __FUNCTION__, (mic.MessageInfoTable == nullptr ? "not " : ""),
            (mic.MessageInfoTable == nullptr ? 0 : mic.MessageInfoTable->MessageCount)));
    }
    else
    {
        DPrintf(0, ("[%s] Not using MSIX\n", __FUNCTION__));
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        NDIS_SG_DMA_DESCRIPTION sgDesc;
        sgDesc.Header.Type = NDIS_OBJECT_TYPE_SG_DMA_DESCRIPTION;
        sgDesc.Header.Revision = NDIS_SG_DMA_DESCRIPTION_REVISION_1;
        sgDesc.Header.Size = sizeof(sgDesc);
        sgDesc.Flags = NDIS_SG_DMA_64_BIT_ADDRESS;
        sgDesc.MaximumPhysicalMapping = 0x10000; // 64K
        sgDesc.ProcessSGListHandler = ProcessSGListHandler;
        sgDesc.SharedMemAllocateCompleteHandler = SharedMemAllocateCompleteHandler;
        sgDesc.ScatterGatherListSize = 0; // OUT value
        status = NdisMRegisterScatterGatherDma(pContext->MiniportHandle, &sgDesc, &pContext->DmaHandle);
        if (status != NDIS_STATUS_SUCCESS)
        {
            DPrintf(0, ("[%s] ERROR: NdisMRegisterScatterGatherDma failed (%X)!\n", __FUNCTION__, status));
        }
        else
        {
            DPrintf(0, ("[%s] SG recommended size %d\n", __FUNCTION__, sgDesc.ScatterGatherListSize));
        }
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        if (NDIS_CONNECT_MESSAGE_BASED == mic.InterruptType)
        {
            pContext->pMSIXInfoTable = mic.MessageInfoTable;
        }
        else if (pContext->bUsingMSIX)
        {
            DPrintf(0, ("[%s] ERROR: Interrupt type %d, message table %p\n",
                __FUNCTION__, mic.InterruptType, mic.MessageInfoTable));
            status = NDIS_STATUS_RESOURCE_CONFLICT;
        }
        ParaNdis6_ApplyOffloadPersistentConfiguration(pContext);
        DebugParseOffloadBits();
    }
    DEBUG_EXIT_STATUS(0, status);
    return status;
}
/**********************************************************
Required NDIS handler
Initialize adapter context, prepare it for operation,
set in PAUSED STATE
Return value:
    SUCCESS or kind of error
***********************************************************/
static NDIS_STATUS ParaNdis6_Initialize(
    NDIS_HANDLE miniportAdapterHandle,
    NDIS_HANDLE miniportDriverContext,
    PNDIS_MINIPORT_INIT_PARAMETERS miniportInitParameters)
{
    NDIS_MINIPORT_ADAPTER_ATTRIBUTES        miniportAttributes;
    NDIS_STATUS  status = NDIS_STATUS_SUCCESS;
    BOOLEAN bNoPauseOnSuspend = FALSE;
    PARANDIS_ADAPTER *pContext;

    UNREFERENCED_PARAMETER(miniportDriverContext);
    DEBUG_ENTRY(0);
#pragma warning( suppress: 28197)
    /* allocate context structure */
    pContext = (PARANDIS_ADAPTER *)
        NdisAllocateMemoryWithTagPriority(
            miniportAdapterHandle,
            sizeof(PARANDIS_ADAPTER),
            PARANDIS_MEMORY_TAG,
            NormalPoolPriority);

    /* This call is for Static Driver Verifier only - has no real functionality*/
    __sdv_save_adapter_context(pContext);

    if (!pContext)
    {
        DPrintf(0, ("[%s] ERROR: Memory allocation failed!\n", __FUNCTION__));
        status = NDIS_STATUS_RESOURCES;
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        /* set mandatory fields which Common use */
        NdisZeroMemory(pContext, sizeof(PARANDIS_ADAPTER));
        pContext->ulUniqueID = NdisInterlockedIncrement(&gID);
        pContext->DriverHandle = DriverHandle;
        pContext->MiniportHandle = miniportAdapterHandle;
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        NdisZeroMemory(&miniportAttributes, sizeof(miniportAttributes));
        miniportAttributes.RegistrationAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
        miniportAttributes.RegistrationAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
        miniportAttributes.RegistrationAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
        miniportAttributes.RegistrationAttributes.MiniportAdapterContext = pContext;
        miniportAttributes.RegistrationAttributes.AttributeFlags =
            // actual for USB
            // NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK
            NDIS_MINIPORT_ATTRIBUTES_HARDWARE_DEVICE |
            NDIS_MINIPORT_ATTRIBUTES_BUS_MASTER;
#ifndef NO_VISTA_POWER_MANAGEMENT
        miniportAttributes.RegistrationAttributes.AttributeFlags |= NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND;
#endif
#if NDIS_SUPPORT_NDIS630
        miniportAttributes.RegistrationAttributes.AttributeFlags |= NDIS_MINIPORT_ATTRIBUTES_NO_PAUSE_ON_SUSPEND;
        bNoPauseOnSuspend = TRUE;
#endif
        miniportAttributes.RegistrationAttributes.CheckForHangTimeInSeconds = 4;
        miniportAttributes.RegistrationAttributes.InterfaceType = NdisInterfacePci;
        status = NdisMSetMiniportAttributes(miniportAdapterHandle, &miniportAttributes);
        if (status != NDIS_STATUS_SUCCESS)
        {
            DPrintf(0, ("[%s] ERROR: NdisMSetMiniportAttributes 1 failed (%X)!\n", __FUNCTION__, status));
        }
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        /* prepare statistics struct for further reports */
        pContext->Statistics.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
        pContext->Statistics.Header.Revision = NDIS_STATISTICS_INFO_REVISION_1;
        pContext->Statistics.Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1;
        /* let Common do all the rest */
        status = ParaNdis_InitializeContext(pContext, miniportInitParameters->AllocatedResources);
        if (status != NDIS_STATUS_SUCCESS)
        {
            DPrintf(0, ("[%s] ERROR: ParaNdis6_InitializeContext failed (%X)!\n", __FUNCTION__, status));
        }
        pContext->bNoPauseOnSuspend = bNoPauseOnSuspend; 
    }

    if (status == NDIS_STATUS_SUCCESS)
    {
        ULONG i;
        NDIS_PNP_CAPABILITIES power60Caps;
#if NDIS_SUPPORT_NDIS620
        NDIS_PM_CAPABILITIES power620Caps;
#endif
#ifdef NO_VISTA_POWER_MANAGEMENT
        pPowerCaps = NULL;
#endif
        ParaNdis_FillPowerCapabilities(&power60Caps);

        NdisZeroMemory(&miniportAttributes, sizeof(miniportAttributes));
        miniportAttributes.GeneralAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
        miniportAttributes.GeneralAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
        miniportAttributes.GeneralAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
#if NDIS_SUPPORT_NDIS620
        miniportAttributes.GeneralAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
        miniportAttributes.GeneralAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
        miniportAttributes.GeneralAttributes.PowerManagementCapabilitiesEx = &power620Caps;
        ParaNdis6_Fill620PowerCapabilities(&power620Caps);
#else
        miniportAttributes.GeneralAttributes.PowerManagementCapabilities = &power60Caps;
#endif
        miniportAttributes.GeneralAttributes.MediaType = NdisMedium802_3;
        miniportAttributes.GeneralAttributes.PhysicalMediumType = NdisPhysicalMedium802_3;
        miniportAttributes.GeneralAttributes.MtuSize = pContext->MaxPacketSize.nMaxDataSize;
        miniportAttributes.GeneralAttributes.LookaheadSize = pContext->MaxPacketSize.nMaxFullSizeOS;
        miniportAttributes.GeneralAttributes.MaxXmitLinkSpeed =
        miniportAttributes.GeneralAttributes.MaxRcvLinkSpeed  =  PARANDIS_FORMAL_LINK_SPEED;
        miniportAttributes.GeneralAttributes.MediaConnectState =
            pContext->bConnected ? MediaConnectStateConnected : MediaConnectStateDisconnected;
        miniportAttributes.GeneralAttributes.XmitLinkSpeed =
        miniportAttributes.GeneralAttributes.RcvLinkSpeed = pContext->bConnected ?
            PARANDIS_FORMAL_LINK_SPEED : NDIS_LINK_SPEED_UNKNOWN;
        miniportAttributes.GeneralAttributes.MediaDuplexState = MediaDuplexStateFull;
        miniportAttributes.GeneralAttributes.MacOptions =
                    NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |       /* NIC has no internal loopback support */
                    NDIS_MAC_OPTION_TRANSFERS_NOT_PEND  |       /* Must be set since using  NdisMIndicateReceivePacket */
                    NDIS_MAC_OPTION_NO_LOOPBACK;                /* NIC has no internal loopback support */
        if (IsPrioritySupported(pContext))
            miniportAttributes.GeneralAttributes.MacOptions |= NDIS_MAC_OPTION_8021P_PRIORITY;
        if (IsVlanSupported(pContext))
            miniportAttributes.GeneralAttributes.MacOptions |= NDIS_MAC_OPTION_8021Q_VLAN;
        miniportAttributes.GeneralAttributes.SupportedPacketFilters = PARANDIS_PACKET_FILTERS;
        miniportAttributes.GeneralAttributes.MaxMulticastListSize = PARANDIS_MULTICAST_LIST_SIZE;
        miniportAttributes.GeneralAttributes.MacAddressLength =     ETH_LENGTH_OF_ADDRESS;

#if PARANDIS_SUPPORT_RSS
        if(pContext->bRSSOffloadSupported)
        {
            miniportAttributes.GeneralAttributes.RecvScaleCapabilities =
                ParaNdis6_RSSCreateConfiguration(
                                                &pContext->RSSParameters,
                                                &pContext->RSSCapabilities,
                                                pContext->RSSMaxQueuesNumber);
            pContext->bRSSInitialized = TRUE;
        }
#endif

        for(i = 0; i < ARRAYSIZE(pContext->ReceiveQueues); i++)
        {
            NdisAllocateSpinLock(&pContext->ReceiveQueues[i].Lock);
            InitializeListHead(&pContext->ReceiveQueues[i].BuffersList);

            pContext->ReceiveQueues[i].BatchReceiveArray =
                ParaNdis_AllocateMemory(pContext, sizeof(*pContext->ReceiveQueues[i].BatchReceiveArray)*pContext->NetMaxReceiveBuffers);
            if(!pContext->ReceiveQueues[i].BatchReceiveArray)
            {
                pContext->ReceiveQueues[i].BatchReceiveArray = &pContext->ReceiveQueues[i].BatchReceiveEmergencyItem;
                pContext->ReceiveQueues[i].BatchReceiveArraySize = 1;
            }
            else
            {
                pContext->ReceiveQueues[i].BatchReceiveArraySize = pContext->NetMaxReceiveBuffers;
            }
        }

        pContext->ReceiveQueuesInitialized = TRUE;

        miniportAttributes.GeneralAttributes.AccessType = NET_IF_ACCESS_BROADCAST;
        miniportAttributes.GeneralAttributes.DirectionType = NET_IF_DIRECTION_SENDRECEIVE;
        miniportAttributes.GeneralAttributes.IfType = IF_TYPE_ETHERNET_CSMACD;
        miniportAttributes.GeneralAttributes.IfConnectorPresent = TRUE;
        miniportAttributes.GeneralAttributes.ConnectionType = NET_IF_CONNECTION_DEDICATED;

        ETH_COPY_NETWORK_ADDRESS(miniportAttributes.GeneralAttributes.PermanentMacAddress, pContext->PermanentMacAddress);
        ETH_COPY_NETWORK_ADDRESS(miniportAttributes.GeneralAttributes.CurrentMacAddress, pContext->CurrentMacAddress);

        ParaNdis6_GetSupportedOid(&miniportAttributes.GeneralAttributes);
        /* update also SupportedStatistics in ready to use statistics block */
        pContext->Statistics.SupportedStatistics = ParaNdis6_GetSupportedStatisticsFlags();
        status = NdisMSetMiniportAttributes(miniportAdapterHandle, &miniportAttributes);
        if (status != NDIS_STATUS_SUCCESS)
        {
            DPrintf(0, ("[%s] ERROR: NdisMSetMiniportAttributes 2 failed (%X)!\n", __FUNCTION__, status));
        }
    }

    if (pContext && status != NDIS_STATUS_SUCCESS && status != NDIS_STATUS_PENDING)
    {
        // no need to cleanup
        NdisFreeMemory(pContext, 0, 0);
        pContext = NULL;
    }

    if (pContext && status == NDIS_STATUS_SUCCESS)
    {
        status = ParaNdis_FinishInitialization(pContext);
        if (status != NDIS_STATUS_SUCCESS)
        {
            ParaNdis_CleanupContext(pContext);
            NdisFreeMemory(pContext, 0, 0);
            pContext = NULL;
        }
    }
    if (pContext && status == NDIS_STATUS_SUCCESS)
    {
        if (NDIS_STATUS_SUCCESS ==
            ParaNdis6_GetRegistrationOffloadInfo(pContext,
                &miniportAttributes.OffloadAttributes))
            status = NdisMSetMiniportAttributes(miniportAdapterHandle, &miniportAttributes);
            if (status != NDIS_STATUS_SUCCESS)
            {
                DPrintf(0, ("[%s] ERROR: NdisMSetMiniportAttributes 3 failed (%X)!\n", __FUNCTION__, status));
            }
    }

    if (pContext && status == NDIS_STATUS_SUCCESS)
    {
        ParaNdis_DebugRegisterMiniport(pContext, TRUE);
    }

    DEBUG_EXIT_STATUS(status ? 0 : 2, status);
    return status;
}
VOID ParaNdis_Resume(PARANDIS_ADAPTER *pContext)
{
	ParaNdis5_StopSend(pContext, FALSE, NULL);
	ParaNdis5_StopReceive(pContext, FALSE, NULL);
	DEBUG_EXIT_STATUS(0, 0);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
    NDIS_STATUS                             status = NDIS_STATUS_FAILURE;
    NDIS_MINIPORT_DRIVER_CHARACTERISTICS    chars;
#ifdef DEBUG_TIMING
    LARGE_INTEGER TickCount;
    LARGE_INTEGER SysTime;
#endif DEBUG_TIMING

    ParaNdis_DebugInitialize();

    DEBUG_ENTRY(0);
    DPrintf(0, (__DATE__ " " __TIME__ "built %d.%d\n", NDIS_MINIPORT_MAJOR_VERSION, NDIS_MINIPORT_MINOR_VERSION));
#ifdef DEBUG_TIMING
    KeQueryTickCount(&TickCount);
    NdisGetCurrentSystemTime(&SysTime);
    DPrintf(0, ("\n%s>> CPU #%d, perf-counter %I64d, tick count %I64d, NDIS_sys_time %I64d\n", __FUNCTION__, KeGetCurrentProcessorNumber(), KeQueryPerformanceCounter(NULL).QuadPart,TickCount.QuadPart, SysTime.QuadPart));
#endif

    NdisZeroMemory(&chars, sizeof(chars));

    chars.Header.Type      = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS;
    chars.Header.Revision  = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1;
    chars.Header.Size      = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1;
    chars.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION;
    chars.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION;
    /* stupid thing, they are at least short */
    chars.MajorDriverVersion = (UCHAR)(PARANDIS_MAJOR_DRIVER_VERSION & 0xFF);
    chars.MinorDriverVersion = (UCHAR)(PARANDIS_MINOR_DRIVER_VERSION & 0xFF);

    // possible value for regular miniport NDIS_WDM_DRIVER - for USB or 1394
    // chars.Flags  = 0;

    chars.InitializeHandlerEx           = ParaNdis6_Initialize;
    chars.HaltHandlerEx                 = ParaNdis6_Halt;
    chars.UnloadHandler                 = ParaNdis6_Unload;
    chars.PauseHandler                  = ParaNdis6_Pause;
    chars.RestartHandler                = ParaNdis6_Restart;
    chars.OidRequestHandler             = ParaNdis6_OidRequest;
    chars.CancelOidRequestHandler       = ParaNdis6_OidCancelRequest;
    chars.SendNetBufferListsHandler     = ParaNdis6_SendNetBufferLists;
    chars.CancelSendHandler             = ParaNdis6_CancelSendNetBufferLists;
    chars.ReturnNetBufferListsHandler   = ParaNdis6_ReturnNetBufferLists;
    chars.CheckForHangHandlerEx         = ParaNdis6_CheckForHang;
    chars.ResetHandlerEx                = ParaNdis6_Reset;
    chars.ShutdownHandlerEx             = ParaNdis6_AdapterShutdown;
    chars.DevicePnPEventNotifyHandler   = ParaNdis6_DevicePnPEvent;
    chars.SetOptionsHandler             = ParaNdis6_SetOptions;

#if NDIS_SUPPORT_NDIS61
    chars.Header.Revision  = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;
    chars.Header.Size      = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;
    chars.DirectOidRequestHandler       = ParaNdis6x_DirectOidRequest;
    chars.CancelDirectOidRequestHandler = ParaNdis6x_CancelDirectOidRequest;
#endif

    status = NdisMRegisterMiniportDriver(
            pDriverObject,
            pRegistryPath,
            NULL,
            &chars,
            &DriverHandle);

    if (status == NDIS_STATUS_SUCCESS)
    {
        RetrieveDriverConfiguration();
    }
    DEBUG_EXIT_STATUS(status ? 0 : 4, status);
    return status;
}