pRxNetDescriptor CParaNdisRX::CreateRxDescriptorOnInit()
{
    //For RX packets we allocate following pages
    //  1 page for virtio header and indirect buffers array
    //  X pages needed to fit maximal length buffer of data
    //  The assumption is virtio header and indirect buffers array fit 1 page
    ULONG ulNumPages = m_Context->MaxPacketSize.nMaxDataSizeHwRx / PAGE_SIZE + 2;

    pRxNetDescriptor p = (pRxNetDescriptor)ParaNdis_AllocateMemory(m_Context, sizeof(*p));
    if (p == NULL) return NULL;

    NdisZeroMemory(p, sizeof(*p));

    p->BufferSGArray = (struct VirtIOBufferDescriptor *)
        ParaNdis_AllocateMemory(m_Context, sizeof(*p->BufferSGArray) * ulNumPages);
    if (p->BufferSGArray == NULL) goto error_exit;

    p->PhysicalPages = (tCompletePhysicalAddress *)
        ParaNdis_AllocateMemory(m_Context, sizeof(*p->PhysicalPages) * ulNumPages);
    if (p->PhysicalPages == NULL) goto error_exit;

    for (p->PagesAllocated = 0; p->PagesAllocated < ulNumPages; p->PagesAllocated++)
    {
        p->PhysicalPages[p->PagesAllocated].size = PAGE_SIZE;
        if (!InitialAllocatePhysicalMemory(&p->PhysicalPages[p->PagesAllocated]))
            goto error_exit;

        p->BufferSGArray[p->PagesAllocated].physAddr = p->PhysicalPages[p->PagesAllocated].Physical;
        p->BufferSGArray[p->PagesAllocated].length = PAGE_SIZE;
    }

    //First page is for virtio header, size needs to be adjusted correspondingly
    p->BufferSGArray[0].length = m_Context->nVirtioHeaderSize;

    //Pre-cache indirect area addresses
    p->IndirectArea.Physical.QuadPart = p->PhysicalPages[0].Physical.QuadPart + m_Context->nVirtioHeaderSize;
    p->IndirectArea.Virtual = RtlOffsetToPointer(p->PhysicalPages[0].Virtual, m_Context->nVirtioHeaderSize);
    p->IndirectArea.size = PAGE_SIZE - m_Context->nVirtioHeaderSize;

    if (!ParaNdis_BindRxBufferToPacket(m_Context, p))
        goto error_exit;

    return p;

error_exit:
    ParaNdis_FreeRxBufferDescriptor(m_Context, p);
    return NULL;
}
/**********************************************************
NDIS5.X handler of power management
***********************************************************/
NDIS_STATUS ParaNdis_OnSetPower(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
{
	NDIS_STATUS status;
	NDIS_DEVICE_POWER_STATE newState;
	DEBUG_ENTRY(0);
	status = ParaNdis_OidSetCopy(pOid, &newState, sizeof(newState));
	if (status == NDIS_STATUS_SUCCESS)
	{
		tPowerWorkItem *pwi = ParaNdis_AllocateMemory(pContext, sizeof(tPowerWorkItem));
		status = NDIS_STATUS_FAILURE;
		if (pwi)
		{
			pwi->pContext = pContext;
			pwi->state    = newState;
			NdisInitializeWorkItem(&pwi->wi, OnSetPowerWorkItem, pwi);
			if (NdisScheduleWorkItem(&pwi->wi) == NDIS_STATUS_SUCCESS)
			{
				status = NDIS_STATUS_PENDING;
			}
			else
				NdisFreeMemory(pwi, 0, 0);
		}
	}
	return status;
}
/*************************************************************
Required NDIS procedure
Called when some procedure (like OID handler) returns PENDING and
does not complete or when CheckForHang return TRUE
*************************************************************/
static NDIS_STATUS ParaNdis5_Reset(
	OUT PBOOLEAN AddressingReset,
	IN NDIS_HANDLE MiniportAdapterContext)
{
	NDIS_STATUS status;
	tGeneralWorkItem *pwi;
	PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
	DEBUG_ENTRY(0);
	ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 1, 0, 0);
	status = NDIS_STATUS_FAILURE;
	pwi = ParaNdis_AllocateMemory(pContext, sizeof(tGeneralWorkItem));
	if (pwi)
	{
		pwi->pContext = pContext;
		NdisInitializeWorkItem(&pwi->wi, OnResetWorkItem, pwi);
		if (NdisScheduleWorkItem(&pwi->wi) == NDIS_STATUS_SUCCESS)
		{
			status = NDIS_STATUS_PENDING;
		}
		else
		{
			NdisFreeMemory(pwi, 0, 0);
		}
	}
	if (status != NDIS_STATUS_PENDING)
	{
		ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 0, status, 0);
	}
	return 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;
}
/********************************************************
Allocate and fill our capabilities, dependent on registry setting
Note than NDIS test of WLK1.2 and 1.3 fail (offloadmisc)
if CS capability indicated and passes if only LSO indicated
********************************************************/
NDIS_STATUS CreateOffloadInfo5Internal(
	PARANDIS_ADAPTER *pContext,
	PVOID *ppInfo,
	PULONG pulSize,
	PCCHAR reason,
	NDIS_TASK_OFFLOAD_HEADER *pHeader)
{
	NDIS_STATUS status = NDIS_STATUS_RESOURCES;
	ULONG size =
		sizeof(NDIS_TASK_OFFLOAD_HEADER) +
		sizeof(NDIS_TASK_OFFLOAD) + sizeof(NDIS_TASK_TCP_IP_CHECKSUM) +
		sizeof(NDIS_TASK_OFFLOAD) + sizeof(NDIS_TASK_TCP_LARGE_SEND);
	*ppInfo = ParaNdis_AllocateMemory(pContext, size);
	if (*ppInfo)
	{
		ULONG flags = 0;
		NDIS_TASK_TCP_IP_CHECKSUM cs;
		NDIS_TASK_TCP_LARGE_SEND lso;
		flags |= GetTcpIpCheckSumCapabilities(pContext, &cs) ? 2 : 0;
		flags |= GetLargeSendCapabilities(pContext, &lso) ? 1 : 0;
		if (flags)
		{
			NDIS_TASK_OFFLOAD_HEADER *ph;
			NDIS_TASK_OFFLOAD *pto;
			UINT i = 0;
			ULONG *pOffset;
			PVOID base;
			*pulSize = size;
			NdisZeroMemory(*ppInfo, size);
			ph = (NDIS_TASK_OFFLOAD_HEADER *)*ppInfo;
			*ph = *pHeader;
			pto = (NDIS_TASK_OFFLOAD *)(ph + 1);
			base = ph;
			pOffset = &ph->OffsetFirstTask;
			ph->OffsetFirstTask = 0;
			do
			{
				if (flags & (1 << i))
				{
					flags &= ~(1 << i);
					pto->Version = NDIS_TASK_OFFLOAD_VERSION;
					pto->Size = sizeof(*pto);
					*pOffset = RtlPointerToOffset(base, pto);
					base = pto;
					pOffset = &pto->OffsetNextTask;
					switch(i)
					{
						case 1:
						{
							NDIS_TASK_TCP_IP_CHECKSUM *pcs = (NDIS_TASK_TCP_IP_CHECKSUM *)pto->TaskBuffer;
							pto->Task = TcpIpChecksumNdisTask;
							pto->TaskBufferLength = sizeof(*pcs);
							NdisMoveMemory(pcs, &cs, sizeof(cs));
							pto = (NDIS_TASK_OFFLOAD *)(pcs + 1);
							break;
						}
						case 0:
						{
							NDIS_TASK_TCP_LARGE_SEND  *pls = (NDIS_TASK_TCP_LARGE_SEND *)pto->TaskBuffer;
							pto->Task = TcpLargeSendNdisTask;
							pto->TaskBufferLength = sizeof(*pls);
							NdisMoveMemory(pls, &lso, sizeof(lso));
							pto = (NDIS_TASK_OFFLOAD *)(pls + 1);
							break;
						}
						default:
							break;
					}
				}
				++i;
			} while (flags);
			status = ParseOffload(pContext, ph, size, FALSE, reason, FALSE);
		}
		else
		{
			NdisFreeMemory(*ppInfo, 0, 0);
			*ppInfo = NULL;
			status = NDIS_STATUS_NOT_SUPPORTED;
		}
	}
	return status;
}
/******************************************************
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;
}
/**********************************************************
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;
}
pRxNetDescriptor CParaNdisRX::CreateRxDescriptorOnInit()
{
    //For RX packets we allocate following pages
    //  1 page for virtio header and indirect buffers array
    //  X pages needed to fit maximal length buffer of data
    //  The assumption is virtio header and indirect buffers array fit 1 page
    ULONG ulNumPages = m_Context->MaxPacketSize.nMaxDataSizeHwRx / PAGE_SIZE + 2;

    pRxNetDescriptor p = (pRxNetDescriptor)ParaNdis_AllocateMemory(m_Context, sizeof(*p));
    if (p == NULL) return NULL;

    NdisZeroMemory(p, sizeof(*p));

    p->BufferSGArray = (struct VirtIOBufferDescriptor *)
        ParaNdis_AllocateMemory(m_Context, sizeof(*p->BufferSGArray) * ulNumPages);
    if (p->BufferSGArray == NULL) goto error_exit;

    p->PhysicalPages = (tCompletePhysicalAddress *)
        ParaNdis_AllocateMemory(m_Context, sizeof(*p->PhysicalPages) * ulNumPages);
    if (p->PhysicalPages == NULL) goto error_exit;

    p->BufferSGLength = 0;
    while (ulNumPages > 0)
    {
        // Allocate the first page separately, the rest can be one contiguous block
        ULONG ulPagesToAlloc = (p->BufferSGLength == 0 ? 1 : ulNumPages);

        while (!ParaNdis_InitialAllocatePhysicalMemory(
                    m_Context,
                    PAGE_SIZE * ulPagesToAlloc,
                    &p->PhysicalPages[p->BufferSGLength]))
        {
            // Retry with half the pages
            if (ulPagesToAlloc == 1)
                goto error_exit;
            else
                ulPagesToAlloc /= 2;
        }

        p->BufferSGArray[p->BufferSGLength].physAddr = p->PhysicalPages[p->BufferSGLength].Physical;
        p->BufferSGArray[p->BufferSGLength].length = p->PhysicalPages[p->BufferSGLength].size;

        ulNumPages -= ulPagesToAlloc;
        p->BufferSGLength++;
    }

    //First page is for virtio header, size needs to be adjusted correspondingly
    p->BufferSGArray[0].length = m_Context->nVirtioHeaderSize;

    ULONG indirectAreaOffset = ALIGN_UP(m_Context->nVirtioHeaderSize, ULONGLONG);
    //Pre-cache indirect area addresses
    p->IndirectArea.Physical.QuadPart = p->PhysicalPages[0].Physical.QuadPart + indirectAreaOffset;
    p->IndirectArea.Virtual = RtlOffsetToPointer(p->PhysicalPages[0].Virtual, indirectAreaOffset);
    p->IndirectArea.size = PAGE_SIZE - indirectAreaOffset;

    if (!ParaNdis_BindRxBufferToPacket(m_Context, p))
        goto error_exit;

    return p;

error_exit:
    ParaNdis_FreeRxBufferDescriptor(m_Context, p);
    return NULL;
}