/* * This file contains NDIS5.X Implementation of adapter driver procedures. * * 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. */ #include "ParaNdis5.h" #ifdef WPP_EVENT_TRACING #include "ParaNdis5-Impl.tmh" #endif /********************************************************** Per-packet information holder ***********************************************************/ #define SEND_ENTRY_FLAG_READY 0x0001 #define SEND_ENTRY_TSO_USED 0x0002 #define SEND_ENTRY_NO_INDIRECT 0x0004 #define SEND_ENTRY_TCP_CS 0x0008 #define SEND_ENTRY_UDP_CS 0x0010 #define SEND_ENTRY_IP_CS 0x0020 typedef struct _tagSendEntry { LIST_ENTRY list; PNDIS_PACKET packet; ULONG flags; ULONG ipTransferUnit; union { ULONG PriorityDataLong; UCHAR PriorityData[4]; }; } tSendEntry; /********************************************************** This defines field in NDIS_PACKET structure to use as holder of our reference pointer for indicated packets ***********************************************************/ #define IDXTOUSE 0 #define REF_MINIPORT(Packet) ((PVOID *)(Packet->MiniportReservedEx + IDXTOUSE * sizeof(PVOID))) /********************************************************** Memory allocation procedure Parameters: context(not used) ULONG ulRequiredSize size of block to allocate Return value: PVOID pointer to block or NULL if failed ***********************************************************/ PVOID ParaNdis_AllocateMemory(PARANDIS_ADAPTER *pContext, ULONG ulRequiredSize) { PVOID p; UNREFERENCED_PARAMETER(pContext); if (NDIS_STATUS_SUCCESS != NdisAllocateMemoryWithTag(&p, ulRequiredSize, PARANDIS_MEMORY_TAG)) p = NULL; if (!p) { DPrintf(0, ("[%s] failed (%d bytes)", __FUNCTION__, ulRequiredSize)); } return p; } /********************************************************** Implementation of "open adapter configuration" operation Parameters: context Return value: NDIS_HANDLE Handle to open configuration or NULL, if failed ***********************************************************/ NDIS_HANDLE ParaNdis_OpenNICConfiguration(PARANDIS_ADAPTER *pContext) { NDIS_STATUS status; NDIS_HANDLE cfg; DEBUG_ENTRY(2); NdisOpenConfiguration(&status, &cfg, pContext->WrapperConfigurationHandle); if (status != NDIS_STATUS_SUCCESS) cfg = NULL; DEBUG_EXIT_STATUS(0, status); return cfg; } void ParaNdis_RestoreDeviceConfigurationAfterReset( PARANDIS_ADAPTER *pContext) { } /********************************************************** Indicates connect/disconnect events Parameters: context BOOLEAN bConnected 1/0 connect/disconnect ***********************************************************/ VOID ParaNdis_IndicateConnect(PARANDIS_ADAPTER *pContext, BOOLEAN bConnected, BOOLEAN bForce) { // indicate disconnect always if (bConnected != pContext->bConnected || bForce) { pContext->bConnected = bConnected; DPrintf(0, ("Indicating %sconnect", bConnected ? "" : "dis")); ParaNdis_DebugHistory(pContext, hopConnectIndication, NULL, bConnected, 0, 0); NdisMIndicateStatus( pContext->MiniportHandle, bConnected ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, 0, 0); NdisMIndicateStatusComplete(pContext->MiniportHandle); } } VOID ParaNdis_SetPowerState(PARANDIS_ADAPTER *pContext, NDIS_DEVICE_POWER_STATE newState) { //NDIS_DEVICE_POWER_STATE prev = pContext->powerState; pContext->powerState = newState; } /********************************************************** Callback of timer for connect indication, if used Parameters: context (on FunctionContext) all the rest are irrelevant ***********************************************************/ static VOID NTAPI OnConnectTimer( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3 ) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)FunctionContext; ParaNdis_ReportLinkStatus(pContext, FALSE); } /********************************************************** NDIS5 implementation of shared memory allocation Parameters: context tCompletePhysicalAddress *pAddresses the structure accumulates all our knowledge about the allocation (size, addresses, cacheability etc) Return value: TRUE if the allocation was successful ***********************************************************/ BOOLEAN ParaNdis_InitialAllocatePhysicalMemory( PARANDIS_ADAPTER *pContext, tCompletePhysicalAddress *pAddresses) { NdisMAllocateSharedMemory( pContext->MiniportHandle, pAddresses->size, (BOOLEAN)pAddresses->IsCached, &pAddresses->Virtual, &pAddresses->Physical); return pAddresses->Virtual != NULL; } /********************************************************** Callback of timer for pending events cleanup after regular DPC processing Parameters: context (on FunctionContext) all the rest are irrelevant ***********************************************************/ static VOID NTAPI OnDPCPostProcessTimer( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3 ) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)FunctionContext; ULONG requiresProcessing; requiresProcessing = ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE); if (requiresProcessing) { // we need to request additional DPC InterlockedOr(&pContext->InterruptStatus, requiresProcessing); NdisSetTimer(&pContext->DPCPostProcessTimer, 10); } } /********************************************************** NDIS5 implementation of shared memory freeing Parameters: context tCompletePhysicalAddress *pAddresses the structure accumulates all our knowledge about the allocation (size, addresses, cacheability etc) filled by ParaNdis_InitialAllocatePhysicalMemory ***********************************************************/ VOID ParaNdis_FreePhysicalMemory( PARANDIS_ADAPTER *pContext, tCompletePhysicalAddress *pAddresses) { NdisMFreeSharedMemory( pContext->MiniportHandle, pAddresses->size, (BOOLEAN)pAddresses->IsCached, pAddresses->Virtual, pAddresses->Physical); } static void DebugParseOffloadBits() { NDIS_TCP_IP_CHECKSUM_PACKET_INFO info; tChecksumCheckResult res; ULONG val = 1; int level = 1; while (val) { info.Value = val; if (info.Receive.NdisPacketIpChecksumFailed) DPrintf(level, ("W.%X=IPCS failed", val)); if (info.Receive.NdisPacketIpChecksumSucceeded) DPrintf(level, ("W.%X=IPCS OK", val)); if (info.Receive.NdisPacketTcpChecksumFailed) DPrintf(level, ("W.%X=TCPCS failed", val)); if (info.Receive.NdisPacketTcpChecksumSucceeded) DPrintf(level, ("W.%X=TCPCS OK", val)); if (info.Receive.NdisPacketUdpChecksumFailed) DPrintf(level, ("W.%X=UDPCS failed", val)); if (info.Receive.NdisPacketUdpChecksumSucceeded) DPrintf(level, ("W.%X=UDPCS OK", val)); val = val << 1; } val = 1; while (val) { res.value = val; if (res.flags.IpFailed) DPrintf(level, ("C.%X=IPCS failed", val)); if (res.flags.IpOK) DPrintf(level, ("C.%X=IPCS OK", val)); if (res.flags.TcpFailed) DPrintf(level, ("C.%X=TCPCS failed", val)); if (res.flags.TcpOK) DPrintf(level, ("C.%X=TCPCS OK", val)); if (res.flags.UdpFailed) DPrintf(level, ("C.%X=UDPCS failed", val)); if (res.flags.UdpOK) DPrintf(level, ("C.%X=UDPCS OK", val)); val = val << 1; } } /********************************************************** Procedure for NDIS5 specific initialization: register interrupt handler allocate pool of packets to indicate allocate pool of buffers to indicate initialize halt event Parameters: context Return value: SUCCESS or failure code ***********************************************************/ NDIS_STATUS NTAPI ParaNdis_FinishSpecificInitialization( PARANDIS_ADAPTER *pContext) { NDIS_STATUS status; UINT nPackets = pContext->NetMaxReceiveBuffers * 2; DEBUG_ENTRY(2); NdisInitializeEvent(&pContext->HaltEvent); InitializeListHead(&pContext->SendQueue); InitializeListHead(&pContext->TxWaitingList); NdisInitializeTimer(&pContext->ConnectTimer, OnConnectTimer, pContext); NdisInitializeTimer(&pContext->DPCPostProcessTimer, OnDPCPostProcessTimer, pContext); status = NdisMRegisterInterrupt( &pContext->Interrupt, pContext->MiniportHandle, pContext->AdapterResources.Vector, pContext->AdapterResources.Level, TRUE, TRUE, NdisInterruptLevelSensitive); if (status == NDIS_STATUS_SUCCESS) { NdisAllocatePacketPool( &status, &pContext->PacketPool, nPackets, PROTOCOL_RESERVED_SIZE_IN_PACKET ); } if (status == NDIS_STATUS_SUCCESS) { NdisAllocateBufferPool( &status, &pContext->BuffersPool, nPackets); } #if !DO_MAP_REGISTERS if (status == NDIS_STATUS_SUCCESS) { status = NdisMInitializeScatterGatherDma( pContext->MiniportHandle, TRUE, 0x10000); pContext->bDmaInitialized = status == NDIS_STATUS_SUCCESS; } #else if (status == NDIS_STATUS_SUCCESS) { status = NdisMAllocateMapRegisters( pContext->MiniportHandle, 0, NDIS_DMA_32BITS, 64, PAGE_SIZE); pContext->bDmaInitialized = status == NDIS_STATUS_SUCCESS; } #endif if (status == NDIS_STATUS_SUCCESS) { DebugParseOffloadBits(); } DEBUG_EXIT_STATUS(status ? 0 : 2, status); return status; } /********************************************************** Procedure of NDIS5-specific cleanup: deregister interrupt free buffer and packet pool Parameters: context ***********************************************************/ VOID ParaNdis_FinalizeCleanup(PARANDIS_ADAPTER *pContext) { if (pContext->Interrupt.InterruptObject) { NdisMDeregisterInterrupt(&pContext->Interrupt); } if (pContext->BuffersPool) { NdisFreeBufferPool(pContext->BuffersPool); } if (pContext->PacketPool) { NdisFreePacketPool(pContext->PacketPool); } #if DO_MAP_REGISTERS if (pContext->bDmaInitialized) { NdisMFreeMapRegisters(pContext->MiniportHandle); } #endif } static FORCEINLINE ULONG MaxNdisBufferDataSize(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDesc) { ULONG size = pBufferDesc->DataInfo.size; if (pContext->bUseMergedBuffers) size -= pContext->nVirtioHeaderSize; return size; } /********************************************************** NDIS5-specific procedure for binding RX buffer to NDIS_PACKET and NDIS_BUFFER Parameters: context pIONetDescriptor pBuffersDesc VirtIO buffer descriptor Return value: TRUE, if bound successfully FALSE, if no buffer or packet can be allocated ***********************************************************/ BOOLEAN ParaNdis_BindBufferToPacket( PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDesc) { NDIS_STATUS status; PNDIS_BUFFER pBuffer = NULL; PNDIS_PACKET Packet = NULL; NdisAllocatePacket(&status, &Packet, pContext->PacketPool); if (status == NDIS_STATUS_SUCCESS) { NdisReinitializePacket(Packet); NdisAllocateBuffer( &status, &pBuffer, pContext->BuffersPool, RtlOffsetToPointer(pBufferDesc->DataInfo.Virtual, pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0), MaxNdisBufferDataSize(pContext, pBufferDesc)); } if (status == NDIS_STATUS_SUCCESS) { PNDIS_PACKET_OOB_DATA pOOB = NDIS_OOB_DATA_FROM_PACKET(Packet); NdisZeroMemory(pOOB, sizeof(NDIS_PACKET_OOB_DATA)); NDIS_SET_PACKET_HEADER_SIZE(Packet, ETH_HEADER_SIZE); NdisChainBufferAtFront(Packet, pBuffer); pBufferDesc->pHolder = Packet; } else { if (pBuffer) NdisFreeBuffer(pBuffer); if (Packet) NdisFreePacket(Packet); } return status == NDIS_STATUS_SUCCESS; } /********************************************************** NDIS5-specific procedure for unbinding previously bound RX buffer from it's NDIS_PACKET and NDIS_BUFFER Parameters: context pIONetDescriptor pBuffersDesc VirtIO buffer descriptor ***********************************************************/ void ParaNdis_UnbindBufferFromPacket( PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDesc) { if (pBufferDesc->pHolder) { PNDIS_BUFFER pBuffer = NULL; PNDIS_PACKET Packet = pBufferDesc->pHolder; pBufferDesc->pHolder = NULL; NdisUnchainBufferAtFront(Packet, &pBuffer); if (pBuffer) { NdisAdjustBufferLength(pBuffer, MaxNdisBufferDataSize(pContext, pBufferDesc)); NdisFreeBuffer(pBuffer); } NdisFreePacket(Packet); } } /********************************************************** NDIS5-specific procedure to indicate received packets Parameters: context pIONetDescriptor pBuffersDescriptor - VirtIO buffer descriptor of data buffer PVOID dataBuffer - data buffer to pass to network stack PULONG pLength - size of received packet. BOOLEAN bPrepareOnly - only return NBL for further indication in batch Return value: TRUE is packet indicated FALSE if not (in this case, the descriptor should be freed now) If priority header is in the packet. it will be removed and *pLength decreased ***********************************************************/ tPacketIndicationType ParaNdis_IndicateReceivedPacket( PARANDIS_ADAPTER *pContext, PVOID dataBuffer, PULONG pLength, BOOLEAN bPrepareOnly, pIONetDescriptor pBuffersDesc) { PNDIS_BUFFER pBuffer = NULL; PNDIS_BUFFER pNoBuffer = NULL; PNDIS_PACKET Packet = pBuffersDesc->pHolder; ULONG length = *pLength; if (Packet) NdisUnchainBufferAtFront(Packet, &pBuffer); if (Packet) NdisUnchainBufferAtFront(Packet, &pNoBuffer); if (pBuffer) { UINT uTotalLength; NDIS_PACKET_8021Q_INFO qInfo; qInfo.Value = NULL; if ((pContext->ulPriorityVlanSetting && length > (ETH_PRIORITY_HEADER_OFFSET + ETH_PRIORITY_HEADER_SIZE)) || length > pContext->MaxPacketSize.nMaxFullSizeOS) { PUCHAR pPriority = (PUCHAR)dataBuffer + ETH_PRIORITY_HEADER_OFFSET; if (ETH_HAS_PRIO_HEADER(dataBuffer)) { if (IsPrioritySupported(pContext)) qInfo.TagHeader.UserPriority = (pPriority[2] & 0xE0) >> 5; if (IsVlanSupported(pContext)) { qInfo.TagHeader.VlanId = (((USHORT)(pPriority[2] & 0x0F)) << 8) | pPriority[3]; if (pContext->VlanId && pContext->VlanId != qInfo.TagHeader.VlanId) { DPrintf(0, ("[%s] Failing unexpected VlanID %d", __FUNCTION__, qInfo.TagHeader.VlanId)); pContext->extraStatistics.framesFilteredOut++; pBuffer = NULL; } } RtlMoveMemory( pPriority, pPriority + ETH_PRIORITY_HEADER_SIZE, length - ETH_PRIORITY_HEADER_OFFSET - ETH_PRIORITY_HEADER_SIZE); length -= ETH_PRIORITY_HEADER_SIZE; if (length > pContext->MaxPacketSize.nMaxFullSizeOS) { DPrintf(0, ("[%s] Can not indicate up packet of %d", __FUNCTION__, length)); pBuffer = NULL; } DPrintf(1, ("[%s] Found priority data %p", __FUNCTION__, qInfo.Value)); pContext->extraStatistics.framesRxPriority++; } } if (pBuffer) { PVOID headerBuffer = pContext->bUseMergedBuffers ? pBuffersDesc->DataInfo.Virtual:pBuffersDesc->HeaderInfo.Virtual; virtio_net_hdr_basic *pHeader = (virtio_net_hdr_basic *)headerBuffer; tChecksumCheckResult csRes; NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) = qInfo.Value; NDIS_SET_PACKET_STATUS(Packet, STATUS_SUCCESS); ParaNdis_PadPacketReceived(dataBuffer, &length); NdisAdjustBufferLength(pBuffer, length); NdisChainBufferAtFront(Packet, pBuffer); NdisQueryPacket(Packet, NULL, NULL, NULL, &uTotalLength); *REF_MINIPORT(Packet) = pBuffersDesc; csRes = ParaNdis_CheckRxChecksum(pContext, pHeader->flags, dataBuffer, length); if (csRes.value) { NDIS_TCP_IP_CHECKSUM_PACKET_INFO qCSInfo; qCSInfo.Value = 0; qCSInfo.Receive.NdisPacketIpChecksumFailed = csRes.flags.IpFailed; qCSInfo.Receive.NdisPacketIpChecksumSucceeded = csRes.flags.IpOK; qCSInfo.Receive.NdisPacketTcpChecksumFailed = csRes.flags.TcpFailed; qCSInfo.Receive.NdisPacketTcpChecksumSucceeded = csRes.flags.TcpOK; qCSInfo.Receive.NdisPacketUdpChecksumFailed = csRes.flags.UdpFailed; qCSInfo.Receive.NdisPacketUdpChecksumSucceeded = csRes.flags.UdpOK; NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpIpChecksumPacketInfo) = (PVOID) (ULONG_PTR) qCSInfo.Value; DPrintf(1, ("Reporting CS %X->%X", csRes.value, qCSInfo.Value)); } DPrintf(4, ("[%s] buffer %p(%d b.)", __FUNCTION__, pBuffersDesc, length)); if (!bPrepareOnly) { NdisMIndicateReceivePacket( pContext->MiniportHandle, &Packet, 1); } } *pLength = length; } if (!pBuffer) { DPrintf(0, ("[%s] Error: %p(%d b.) with packet %p", __FUNCTION__, pBuffersDesc, length, Packet)); Packet = NULL; } if (pNoBuffer) { DPrintf(0, ("[%s] Error: %p(%d b.) with packet %p, buf %p,%p", __FUNCTION__, pBuffersDesc, length, Packet, pBuffer, pNoBuffer)); } return Packet; } VOID ParaNdis_IndicateReceivedBatch( PARANDIS_ADAPTER *pContext, tPacketIndicationType *pBatch, ULONG nofPackets) { NdisMIndicateReceivePacket( pContext->MiniportHandle, pBatch, nofPackets); } static FORCEINLINE void GET_NUMBER_OF_SG_ELEMENTS(PNDIS_PACKET Packet, UINT *pNum) { PSCATTER_GATHER_LIST pSGList; pSGList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo); if (pSGList) { *pNum = pSGList->NumberOfElements; } } /********************************************************** Complete TX packets to NDIS with status, indicated inside packet Parameters: context PNDIS_PACKET Packet packet to complete ***********************************************************/ static void CompletePacket(PARANDIS_ADAPTER *pContext, PNDIS_PACKET Packet) { LONG lRestToReturn; NDIS_STATUS status = NDIS_GET_PACKET_STATUS(Packet); lRestToReturn = NdisInterlockedDecrement(&pContext->NetTxPacketsToReturn); ParaNdis_DebugHistory(pContext, hopSendComplete, Packet, 0, lRestToReturn, status); NdisMSendComplete(pContext->MiniportHandle, Packet, status); } /********************************************************** Copy data from specified packet to VirtIO buffer, minimum 60 bytes Parameters: PNDIS_PACKET Packet packet to copy data from PVOID dest destination to copy ULONG maxSize maximal size of destination Return value: size = number of bytes copied if 0, the packet is not transmitted and should be dropped ( should never happen) request ***********************************************************/ tCopyPacketResult ParaNdis_PacketCopier( PNDIS_PACKET Packet, PVOID dest, ULONG maxSize, PVOID refValue, BOOLEAN bPreview) { PNDIS_BUFFER pBuffer; ULONG PriorityDataLong = ((tSendEntry *)refValue)->PriorityDataLong; tCopyPacketResult result; /* the copier called also for getting Ethernet header for statistics, when the transfer uses SG table */ UINT uLength = 0; ULONG nCopied = 0; ULONG ulToCopy = 0; if (bPreview) PriorityDataLong = 0; NdisQueryPacket(Packet, NULL, NULL, &pBuffer, (PUINT)&ulToCopy); if (ulToCopy > maxSize) ulToCopy = bPreview ? maxSize : 0; while (pBuffer && ulToCopy) { PVOID VirtualAddress = NULL; NdisQueryBufferSafe(pBuffer, &VirtualAddress, &uLength, NormalPagePriority); if (!VirtualAddress) { /* the packet copy failed */ nCopied = 0; break; } if(uLength) { // Copy the data. if (uLength > ulToCopy) uLength = ulToCopy; ulToCopy -= uLength; if ((PriorityDataLong & 0xFFFF) && nCopied < ETH_PRIORITY_HEADER_OFFSET && (nCopied + uLength) >= ETH_PRIORITY_HEADER_OFFSET) { ULONG ulCopyNow = ETH_PRIORITY_HEADER_OFFSET - nCopied; NdisMoveMemory(dest, VirtualAddress, ulCopyNow); dest = (PUCHAR)dest + ulCopyNow; VirtualAddress = (PUCHAR)VirtualAddress + ulCopyNow; NdisMoveMemory(dest, &PriorityDataLong, 4); nCopied += 4; dest = (PCHAR)dest + 4; ulCopyNow = uLength - ulCopyNow; if (ulCopyNow) NdisMoveMemory(dest, VirtualAddress, ulCopyNow); dest = (PCHAR)dest + ulCopyNow; nCopied += uLength; } else { NdisMoveMemory(dest, VirtualAddress, uLength); nCopied += uLength; dest = (PUCHAR)dest + uLength; } } NdisGetNextBuffer(pBuffer, &pBuffer); } DEBUG_EXIT_STATUS(4, nCopied); result.size = nCopied; return result; } /********************************************************** Callback on finished Tx descriptor ***********************************************************/ VOID ParaNdis_OnTransmitBufferReleased(PARANDIS_ADAPTER *pContext, IONetDescriptor *pDesc) { tSendEntry *pEntry = (tSendEntry *)pDesc->ReferenceValue; if (pEntry) { DPrintf(2, ("[%s] Entry %p (packet %p, %d buffers) ready!", __FUNCTION__, pEntry, pEntry->packet, pDesc->nofUsedBuffers)); pEntry->flags |= SEND_ENTRY_FLAG_READY; pDesc->ReferenceValue = NULL; ParaNdis_DebugHistory(pContext, hopBufferSent, pEntry->packet, 0, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors); } else { ParaNdis_DebugHistory(pContext, hopBufferSent, NULL, 0, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors); DPrintf(0, ("[%s] ERROR: Send Entry not set!", __FUNCTION__)); } } static FORCEINLINE ULONG CalculateTotalOffloadSize( ULONG packetSize, ULONG mss, ULONG ipheaderOffset, ULONG maxPossiblePacketSize, tTcpIpPacketParsingResult packetReview) { ULONG ul = 0; ULONG tcpipHeaders = packetReview.XxpIpHeaderSize; ULONG allHeaders = tcpipHeaders + ipheaderOffset; if (tcpipHeaders && (mss + allHeaders) <= maxPossiblePacketSize) { ULONG nFragments = (packetSize - allHeaders)/mss; ULONG last = (packetSize - allHeaders)%mss; ul = nFragments * (mss + allHeaders) + last + (last ? allHeaders : 0); } DPrintf(1, ("[%s]%s %d/%d, headers %d)", __FUNCTION__, !ul ? "ERROR:" : "", ul, mss, allHeaders)); return ul; } /********************************************************** Maps the HW buffers of the packet into entries of VirtIO queue Parameters: miniport context PNDIS_PACKET Packet packet to copy data from PVOID ReferenceValue - tSendEntry * of the packet VirtIOBufferDescriptor buffers = array of buffers to map packet buffers (it contains number of SG entries >= number of hw elements in the packet) pIONetDescriptor pDesc - holder of VirtIO header and reserved data buffer for possible replacement of one or more HW buffers Returns @pMapperResult: (zeroed before call) .usBuffersMapped - number of buffers mapped (one of them may be our own) .ulDataSize - number of bytes to report as transmitted (802.1P tag is not counted) .usBufferSpaceUsed - number of bytes used in data space of pIONetDescriptor pDesc ***********************************************************/ VOID ParaNdis_PacketMapper( PARANDIS_ADAPTER *pContext, PNDIS_PACKET packet, PVOID ReferenceValue, struct VirtIOBufferDescriptor *buffers, pIONetDescriptor pDesc, tMapperResult *pMapperResult) { tSendEntry *pSendEntry = (tSendEntry *)ReferenceValue; ULONG PriorityDataLong = pSendEntry->PriorityDataLong; PSCATTER_GATHER_LIST pSGList = NDIS_PER_PACKET_INFO_FROM_PACKET(packet, ScatterGatherListPacketInfo); SCATTER_GATHER_ELEMENT *pSGElements = pSGList->Elements; if (pSGList && pSGList->NumberOfElements) { UINT i, lengthGet = 0, lengthPut = 0, nCompleteBuffersToSkip = 0, nBytesSkipInFirstBuffer = 0; if (pSendEntry->flags & (SEND_ENTRY_TSO_USED | SEND_ENTRY_TCP_CS | SEND_ENTRY_UDP_CS | SEND_ENTRY_IP_CS)) lengthGet = pContext->Offload.ipHeaderOffset + MAX_IPV4_HEADER_SIZE + sizeof(TCPHeader); if (PriorityDataLong && !lengthGet) lengthGet = ETH_HEADER_SIZE; if (lengthGet) { ULONG len = 0; for (i = 0; i < pSGList->NumberOfElements; ++i) { len += pSGElements[i].Length; if (len > lengthGet) { nBytesSkipInFirstBuffer = pSGList->Elements[i].Length - (len - lengthGet); break; } DPrintf(2, ("[%s] skipping buffer %d of %d", __FUNCTION__, nCompleteBuffersToSkip, pSGElements[i].Length)); nCompleteBuffersToSkip++; } // just for case of UDP packet shorter than TCP header if (lengthGet > len) lengthGet = len; lengthPut = lengthGet + (PriorityDataLong ? ETH_PRIORITY_HEADER_SIZE : 0); } if (lengthPut > pDesc->DataInfo.size) { DPrintf(0, ("[%s] ERROR: can not substitute %d bytes, sending as is", __FUNCTION__, lengthPut)); nCompleteBuffersToSkip = 0; nBytesSkipInFirstBuffer = 0; lengthGet = lengthPut = 0; } if (lengthPut) { // we replace 1 or more HW buffers with one buffer preallocated for data buffers->physAddr = pDesc->DataInfo.Physical; buffers->length = lengthPut; pMapperResult->usBufferSpaceUsed = (USHORT)lengthPut; pMapperResult->ulDataSize += lengthGet; pMapperResult->usBuffersMapped = (USHORT)(pSGList->NumberOfElements - nCompleteBuffersToSkip + 1); pSGElements += nCompleteBuffersToSkip; buffers++; DPrintf(1, ("[%s](%d bufs) skip %d buffers + %d bytes", __FUNCTION__, pSGList->NumberOfElements, nCompleteBuffersToSkip, nBytesSkipInFirstBuffer)); } else { pMapperResult->usBuffersMapped = (USHORT)pSGList->NumberOfElements; } for (i = nCompleteBuffersToSkip; i < pSGList->NumberOfElements; ++i) { if (nBytesSkipInFirstBuffer) { buffers->physAddr.QuadPart = pSGElements->Address.QuadPart + nBytesSkipInFirstBuffer; buffers->length = pSGElements->Length - nBytesSkipInFirstBuffer; DPrintf(2, ("[%s] using HW buffer %d of %d-%d", __FUNCTION__, i, pSGElements->Length, nBytesSkipInFirstBuffer)); nBytesSkipInFirstBuffer = 0; } else { buffers->physAddr = pSGElements->Address; buffers->length = pSGElements->Length; } pMapperResult->ulDataSize += buffers->length; pSGElements++; buffers++; } if (lengthPut) { PVOID pBuffer = pDesc->DataInfo.Virtual; PVOID pIpHeader = RtlOffsetToPointer(pBuffer, pContext->Offload.ipHeaderOffset); ParaNdis_PacketCopier(packet, pBuffer, lengthGet, ReferenceValue, TRUE); if (pSendEntry->flags & SEND_ENTRY_TSO_USED) { tTcpIpPacketParsingResult packetReview; ULONG dummyTransferSize = 0; USHORT saveBuffers = pMapperResult->usBuffersMapped; ULONG flags = pcrIpChecksum | pcrTcpChecksum | pcrFixIPChecksum | pcrFixPHChecksum; pMapperResult->usBuffersMapped = 0; packetReview = ParaNdis_CheckSumVerify( pIpHeader, lengthGet - pContext->Offload.ipHeaderOffset, flags, __FUNCTION__); /* uncomment to verify */ /* packetReview = ParaNdis_CheckSumVerify( pIpHeader, lengthGet - pContext->Offload.ipHeaderOffset, pcrIpChecksum | pcrTcpChecksum, __FUNCTION__); */ if (packetReview.ipCheckSum == ppresCSOK || packetReview.fixedIpCS) { dummyTransferSize = CalculateTotalOffloadSize( pMapperResult->ulDataSize, pSendEntry->ipTransferUnit, pContext->Offload.ipHeaderOffset, pContext->MaxPacketSize.nMaxFullSizeOS, packetReview); } else { DPrintf(0, ("[%s] ERROR locating IP header in %d bytes(IP header of %d)", __FUNCTION__, lengthGet, packetReview.ipHeaderSize)); } NDIS_PER_PACKET_INFO_FROM_PACKET(packet, TcpLargeSendPacketInfo) = (PVOID)(ULONG_PTR)dummyTransferSize; if (dummyTransferSize) { virtio_net_hdr_basic *pheader = pDesc->HeaderInfo.Virtual; unsigned short addPriorityLen = PriorityDataLong ? ETH_PRIORITY_HEADER_SIZE : 0; pheader->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; pheader->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; pheader->hdr_len = (USHORT)(packetReview.XxpIpHeaderSize + pContext->Offload.ipHeaderOffset) + addPriorityLen; pheader->gso_size = (USHORT)pSendEntry->ipTransferUnit; pheader->csum_start = (USHORT)pContext->Offload.ipHeaderOffset + (USHORT)packetReview.ipHeaderSize + addPriorityLen; pheader->csum_offset = TCP_CHECKSUM_OFFSET; pMapperResult->usBuffersMapped = saveBuffers; } } else if (pSendEntry->flags & SEND_ENTRY_IP_CS) { ParaNdis_CheckSumVerify( pIpHeader, lengthGet - pContext->Offload.ipHeaderOffset, pcrIpChecksum | pcrFixIPChecksum, __FUNCTION__); } if (PriorityDataLong && pMapperResult->usBuffersMapped) { RtlMoveMemory( RtlOffsetToPointer(pBuffer, ETH_PRIORITY_HEADER_OFFSET + ETH_PRIORITY_HEADER_SIZE), RtlOffsetToPointer(pBuffer, ETH_PRIORITY_HEADER_OFFSET), lengthGet - ETH_PRIORITY_HEADER_OFFSET ); NdisMoveMemory( RtlOffsetToPointer(pBuffer, ETH_PRIORITY_HEADER_OFFSET), &PriorityDataLong, sizeof(ETH_PRIORITY_HEADER_SIZE)); DPrintf(1, ("[%s] Populated priority value %lX", __FUNCTION__, PriorityDataLong)); } } } } static void InitializeTransferParameters(tTxOperationParameters *pParams, tSendEntry *pEntry) { ULONG flags = (pEntry->flags & SEND_ENTRY_TSO_USED) ? pcrLSO : 0; if (pEntry->flags & SEND_ENTRY_NO_INDIRECT) flags |= pcrNoIndirect; NdisQueryPacket(pEntry->packet, &pParams->nofSGFragments, NULL, NULL, (PUINT)&pParams->ulDataSize); pParams->ReferenceValue = pEntry; pParams->packet = pEntry->packet; pParams->offloadMss = (pEntry->flags & SEND_ENTRY_TSO_USED) ? pEntry->ipTransferUnit : 0; // on NDIS5 it is unknown pParams->tcpHeaderOffset = 0; // fills only if SGList present in the packet GET_NUMBER_OF_SG_ELEMENTS(pEntry->packet, &pParams->nofSGFragments); if (NDIS_GET_PACKET_PROTOCOL_TYPE(pEntry->packet) == NDIS_PROTOCOL_ID_TCP_IP) { flags |= pcrIsIP; if (pEntry->flags & SEND_ENTRY_TCP_CS) { flags |= pcrTcpChecksum; } if (pEntry->flags & SEND_ENTRY_UDP_CS) { flags |= pcrUdpChecksum; } if (pEntry->flags & SEND_ENTRY_IP_CS) { flags |= pcrIpChecksum; } } if (pEntry->PriorityDataLong) flags |= pcrPriorityTag; pParams->flags = flags; } BOOLEAN ParaNdis_ProcessTx( PARANDIS_ADAPTER *pContext, BOOLEAN IsDpc, BOOLEAN IsInterrupt) { LIST_ENTRY DoneList; BOOLEAN bDoKick = FALSE; UINT nBuffersSent = 0, nBytesSent = 0; BOOLEAN bDataAvailable = FALSE; tSendEntry *pEntry; ONPAUSECOMPLETEPROC CallbackToCall = NULL; InitializeListHead(&DoneList); UNREFERENCED_PARAMETER(IsDpc); NdisAcquireSpinLock(&pContext->SendLock); ParaNdis_DebugHistory(pContext, hopTxProcess, NULL, 1, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors); do { if(IsTimeToReleaseTx(pContext)) { // release some buffers ParaNdis_VirtIONetReleaseTransmitBuffers(pContext); } pEntry = NULL; if (!IsListEmpty(&pContext->SendQueue)) { tCopyPacketResult result; tTxOperationParameters Params; pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue); InitializeTransferParameters(&Params, pEntry); bDataAvailable = TRUE; result = ParaNdis_DoSubmitPacket(pContext, &Params); if (result.error == cpeNoBuffer) { // can not send now, try next time InsertHeadList(&pContext->SendQueue, &pEntry->list); pEntry = NULL; } else if (result.error == cpeNoIndirect) { InsertHeadList(&pContext->SendQueue, &pEntry->list); pEntry->flags |= SEND_ENTRY_NO_INDIRECT; } else { InsertTailList(&pContext->TxWaitingList, &pEntry->list); ParaNdis_DebugHistory(pContext, hopSubmittedPacket, pEntry->packet, 0, result.error, Params.flags); if (!result.size) { NDIS_STATUS status = NDIS_STATUS_FAILURE; DPrintf(0, ("[%s] ERROR %d copying packet!", __FUNCTION__, result.error)); if (result.error == cpeTooLarge) { status = NDIS_STATUS_BUFFER_OVERFLOW; pContext->Statistics.ifOutErrors++; } NDIS_SET_PACKET_STATUS(pEntry->packet, status); pEntry->flags |= SEND_ENTRY_FLAG_READY; // do not worry, go to the next one } else { nBuffersSent++; nBytesSent += result.size; DPrintf(2, ("[%s] Scheduled packet %p, entry %p(%d bytes)!", __FUNCTION__, pEntry->packet, pEntry, result.size)); } } } } while (pEntry); if (nBuffersSent) { if(IsInterrupt) { bDoKick = TRUE; } else { #ifdef PARANDIS_TEST_TX_KICK_ALWAYS virtqueue_kick_always(pContext->NetSendQueue); #else virtqueue_kick(pContext->NetSendQueue); #endif } DPrintf(2, ("[%s] sent down %d p.(%d b.)", __FUNCTION__, nBuffersSent, nBytesSent)); } else if (bDataAvailable) { DPrintf(2, ("[%s] nothing sent", __FUNCTION__)); } /* now check the waiting list of packets */ while (!IsListEmpty(&pContext->TxWaitingList)) { pEntry = (tSendEntry *)RemoveHeadList(&pContext->TxWaitingList); if (pEntry->flags & SEND_ENTRY_FLAG_READY) { InsertTailList(&DoneList, &pEntry->list); } else { InsertHeadList(&pContext->TxWaitingList, &pEntry->list); break; } } if (IsListEmpty(&pContext->TxWaitingList) && pContext->SendState == srsPausing && pContext->SendPauseCompletionProc) { CallbackToCall = pContext->SendPauseCompletionProc; pContext->SendPauseCompletionProc = NULL; pContext->SendState = srsDisabled; ParaNdis_DebugHistory(pContext, hopInternalSendPause, NULL, 0, 0, 0); } NdisReleaseSpinLock(&pContext->SendLock); while (!IsListEmpty(&DoneList)) { pEntry = (tSendEntry *)RemoveHeadList(&DoneList); CompletePacket(pContext, pEntry->packet); NdisFreeMemory(pEntry, 0, 0); } if (CallbackToCall) CallbackToCall(pContext); return bDoKick; } /********************************************************** NDIS releases packets previously indicated by miniport Free the packet's buffer and the packet back to their pools Returns VirtIO buffer back to queue of free blocks Parameters: context IN PNDIS_PACKET Packet returned packet ***********************************************************/ VOID NTAPI ParaNdis5_ReturnPacket(IN NDIS_HANDLE MiniportAdapterContext,IN PNDIS_PACKET Packet) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext; pIONetDescriptor pBufferDescriptor; pBufferDescriptor = (pIONetDescriptor) *REF_MINIPORT(Packet); DPrintf(4, ("[%s] buffer %p", __FUNCTION__, pBufferDescriptor)); NdisAcquireSpinLock(&pContext->ReceiveLock); pContext->ReuseBufferProc(pContext, pBufferDescriptor); NdisReleaseSpinLock(&pContext->ReceiveLock); } static __inline tSendEntry * PrepareSendEntry(PARANDIS_ADAPTER *pContext, PNDIS_PACKET Packet, ULONG len) { ULONG mss = (ULONG)(ULONG_PTR)NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo); UINT protocol = NDIS_GET_PACKET_PROTOCOL_TYPE(Packet); LPCSTR errorFmt = NULL; LPCSTR offloadName = "NO offload"; tSendEntry *pse = (tSendEntry *)ParaNdis_AllocateMemory(pContext, sizeof(tSendEntry)); if (pse) { NDIS_PACKET_8021Q_INFO qInfo; pse->packet = Packet; pse->flags = 0; pse->PriorityDataLong = 0; pse->ipTransferUnit = len; //pse->fullTCPCheckSum = 0; qInfo.Value = pContext->ulPriorityVlanSetting ? NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) : NULL; if (!qInfo.TagHeader.VlanId) qInfo.TagHeader.VlanId = pContext->VlanId; if (qInfo.TagHeader.CanonicalFormatId || !IsValidVlanId(pContext, qInfo.TagHeader.VlanId)) { DPrintf(0, ("[%s] Discarding priority tag %p", __FUNCTION__, qInfo.Value)); errorFmt = "invalid priority tag"; } else if (qInfo.Value) { // ignore priority, if configured if (!IsPrioritySupported(pContext)) qInfo.TagHeader.UserPriority = 0; // ignore VlanId, if specified if (!IsVlanSupported(pContext)) qInfo.TagHeader.VlanId = 0; SetPriorityData(pse->PriorityData, qInfo.TagHeader.UserPriority, qInfo.TagHeader.VlanId); DPrintf(1, ("[%s] Populated priority tag %p", __FUNCTION__, qInfo.Value)); } if (!errorFmt && !mss && len > pContext->MaxPacketSize.nMaxFullSizeOS) { DPrintf(0, ("[%s] Request for offload with NO MSS, lso %d, ipheader %d", __FUNCTION__, pContext->Offload.flags.fTxLso, pContext->Offload.ipHeaderOffset)); if (pContext->Offload.flags.fTxLso && pContext->Offload.ipHeaderOffset) { mss = pContext->MaxPacketSize.nMaxFullSizeOS; } else errorFmt = "illegal LSO request"; } if (errorFmt) { // already failed } else if (mss > pContext->MaxPacketSize.nMaxFullSizeOS) errorFmt = "mss is too big"; else if (len > 0xFFFF) errorFmt = "packet is bigger than we able to send"; else if (mss && pContext->Offload.flags.fTxLso) { offloadName = "LSO"; pse->ipTransferUnit = mss; pse->flags |= SEND_ENTRY_TSO_USED; // todo: move to common space // to transmit 'len' with 'mss' we usually need 2 additional buffers if ((len / mss + 3) > pContext->maxFreeHardwareBuffers) errorFmt = "packet too big to fragment"; else if (len < pContext->Offload.ipHeaderOffset) errorFmt = "ip offset is bigger than packet"; else if (protocol != NDIS_PROTOCOL_ID_TCP_IP) errorFmt = "attempt to offload non-IP packet"; else if (mss < pContext->Offload.ipHeaderOffset) errorFmt = "mss is too small"; } else { // unexpected CS requests we do not fail - WHQL expects us to send them as is NDIS_TCP_IP_CHECKSUM_PACKET_INFO csInfo; csInfo.Value = (ULONG)(ULONG_PTR)NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpIpChecksumPacketInfo); if (csInfo.Transmit.NdisPacketChecksumV4) { if (csInfo.Transmit.NdisPacketTcpChecksum) { offloadName = "TCP CS"; if (pContext->Offload.flags.fTxTCPChecksum) pse->flags |= SEND_ENTRY_TCP_CS; else errorFmt = "TCP CS requested but not enabled"; } if (csInfo.Transmit.NdisPacketUdpChecksum) { offloadName = "UDP CS"; if (pContext->Offload.flags.fTxUDPChecksum) pse->flags |= SEND_ENTRY_UDP_CS; else errorFmt = "UDP CS requested but not enabled"; } if (csInfo.Transmit.NdisPacketIpChecksum) { if (pContext->Offload.flags.fTxIPChecksum) pse->flags |= SEND_ENTRY_IP_CS; else errorFmt = "IP CS requested but not enabled"; } if (errorFmt) { DPrintf(0, ("[%s] ERROR: %s (len %d)", __FUNCTION__, errorFmt, len)); errorFmt = NULL; } } } } if (errorFmt) { DPrintf(0, ("[%s] ERROR: %s (len %d, mss %d)", __FUNCTION__, errorFmt, len, mss)); if (pse) NdisFreeMemory(pse, 0, 0); pse = NULL; } else { NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo) = (PVOID)(ULONG_PTR)0; DPrintf(1, ("[%s] Sending packet of %d with %s", __FUNCTION__, len, offloadName)); if (pContext->bDoIPCheckTx) { tTcpIpPacketParsingResult res; VOID *pcopy = ParaNdis_AllocateMemory(pContext, len); ParaNdis_PacketCopier(pse->packet, pcopy, len, pse, TRUE); res = ParaNdis_CheckSumVerify( RtlOffsetToPointer(pcopy, pContext->Offload.ipHeaderOffset), len, pcrAnyChecksum/* | pcrFixAnyChecksum*/, __FUNCTION__); /* if (res.xxpStatus == ppresXxpKnown) { TCPHeader *ptcp = (TCPHeader *) RtlOffsetToPointer(pcopy, pContext->Offload.ipHeaderOffset + res.ipHeaderSize); pse->fullTCPCheckSum = ptcp->tcp_xsum; } */ NdisFreeMemory(pcopy, 0, 0); } } return pse; } /********************************************************** NDIS sends us packets Queues packets internally and calls the procedure to process the queue Parameters: context IN PPNDIS_PACKET PacketArray Array of packets to send IN UINT NumberOfPackets number of packets ***********************************************************/ VOID NTAPI ParaNdis5_SendPackets(IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets) { UINT i; LIST_ENTRY FailedList, DoneList; PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext; InitializeListHead(&FailedList); InitializeListHead(&DoneList); DPrintf(3, ("[%s] %d packets", __FUNCTION__, NumberOfPackets)); ParaNdis_DebugHistory(pContext, hopSend, NULL, 1, NumberOfPackets, 0); NdisAcquireSpinLock(&pContext->SendLock); for (i = 0; i < NumberOfPackets; ++i) { UINT uPacketLength = 0; NdisQueryPacketLength(PacketArray[i], &uPacketLength); NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_SUCCESS); NdisInterlockedIncrement(&pContext->NetTxPacketsToReturn); if (!pContext->bSurprizeRemoved && pContext->bConnected && pContext->SendState == srsEnabled && uPacketLength) { tSendEntry *pse = PrepareSendEntry(pContext, PacketArray[i], uPacketLength); if (!pse) { NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_FAILURE); CompletePacket(pContext, PacketArray[i]); } else { UINT nFragments = 0; GET_NUMBER_OF_SG_ELEMENTS(PacketArray[i], &nFragments); ParaNdis_DebugHistory(pContext, hopSendPacketMapped, PacketArray[i], 0, nFragments, 0); InsertTailList(&pContext->SendQueue, &pse->list); } } else { NDIS_STATUS status = NDIS_STATUS_FAILURE; if (pContext->bSurprizeRemoved) status = NDIS_STATUS_NOT_ACCEPTED; NDIS_SET_PACKET_STATUS(PacketArray[i], status); CompletePacket(pContext, PacketArray[i]); DPrintf(1, ("[%s] packet of %d rejected", __FUNCTION__, uPacketLength)); } } NdisReleaseSpinLock(&pContext->SendLock); ParaNdis_ProcessTx(pContext, FALSE, FALSE); } /********************************************************** NDIS procedure, not easy to test NDIS asks us to cancel packets with specified CancelID Parameters: context PVOID CancelId ID to cancel ***********************************************************/ VOID NTAPI ParaNdis5_CancelSendPackets(IN NDIS_HANDLE MiniportAdapterContext,IN PVOID CancelId) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext; LIST_ENTRY DoneList, KeepList; UINT n = 0; tSendEntry *pEntry; DEBUG_ENTRY(0); InitializeListHead(&DoneList); InitializeListHead(&KeepList); NdisAcquireSpinLock(&pContext->SendLock); while ( !IsListEmpty(&pContext->SendQueue)) { PNDIS_PACKET Packet; pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue); Packet = pEntry->packet; if (NDIS_GET_PACKET_CANCEL_ID(Packet) == CancelId) { InsertTailList(&DoneList, &pEntry->list); ++n; } else InsertTailList(&KeepList, &pEntry->list); } while ( !IsListEmpty(&KeepList)) { pEntry = (tSendEntry *)RemoveHeadList(&KeepList); InsertTailList(&pContext->SendQueue, &pEntry->list); } NdisReleaseSpinLock(&pContext->SendLock); while (!IsListEmpty(&DoneList)) { pEntry = (tSendEntry *)RemoveHeadList(&DoneList); NDIS_SET_PACKET_STATUS(pEntry->packet, NDIS_STATUS_REQUEST_ABORTED); CompletePacket(pContext, pEntry->packet); NdisFreeMemory(pEntry, 0, 0); } DEBUG_EXIT_STATUS(0, n); } /********************************************************** Request to pause or resume data transmit if stopped, all the packets in internal queue are returned Parameters: context BOOLEAN bStop 1/0 - top or resume ***********************************************************/ NDIS_STATUS ParaNdis5_StopSend(PARANDIS_ADAPTER *pContext, BOOLEAN bStop, ONPAUSECOMPLETEPROC Callback) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; if (bStop) { LIST_ENTRY DoneList; tSendEntry *pEntry; DEBUG_ENTRY(0); ParaNdis_DebugHistory(pContext, hopInternalSendPause, NULL, 1, 0, 0); InitializeListHead(&DoneList); NdisAcquireSpinLock(&pContext->SendLock); if (IsListEmpty(&pContext->TxWaitingList)) { pContext->SendState = srsDisabled; while (!IsListEmpty(&pContext->SendQueue)) { pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue); InsertTailList(&DoneList, &pEntry->list); } ParaNdis_DebugHistory(pContext, hopInternalSendPause, NULL, 0, 0, 0); } else { pContext->SendState = srsPausing; pContext->SendPauseCompletionProc = Callback; status = NDIS_STATUS_PENDING; while (!IsListEmpty(&pContext->SendQueue)) { pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue); pEntry->flags |= SEND_ENTRY_FLAG_READY; InsertTailList(&pContext->TxWaitingList, &pEntry->list); } } NdisReleaseSpinLock(&pContext->SendLock); while (!IsListEmpty(&DoneList)) { pEntry = (tSendEntry *)RemoveHeadList(&DoneList); NDIS_SET_PACKET_STATUS(pEntry->packet, NDIS_STATUS_REQUEST_ABORTED); CompletePacket(pContext, pEntry->packet); NdisFreeMemory(pEntry, 0, 0); } } else { pContext->SendState = srsEnabled; ParaNdis_DebugHistory(pContext, hopInternalSendResume, NULL, 0, 0, 0); } return status; } /********************************************************** Pause or resume receive operation: Parameters: context BOOLEAN bStop 1/0 - pause or resume ONPAUSECOMPLETEPROC Callback callback to call, if not completed immediately Return value: SUCCESS, if there is no RX packets under NDIS management PENDING, if we need to wait until NDIS returns us packets ***********************************************************/ NDIS_STATUS ParaNdis5_StopReceive( PARANDIS_ADAPTER *pContext, BOOLEAN bStop, ONPAUSECOMPLETEPROC Callback ) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; if (bStop) { ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 1, 0, 0); NdisAcquireSpinLock(&pContext->ReceiveLock); if (IsListEmpty(&pContext->NetReceiveBuffersWaiting)) { pContext->ReceiveState = srsDisabled; ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 0, 0, 0); } else { pContext->ReceiveState = srsPausing; pContext->ReceivePauseCompletionProc = Callback; status = NDIS_STATUS_PENDING; } NdisReleaseSpinLock(&pContext->ReceiveLock); } else { pContext->ReceiveState = srsEnabled; ParaNdis_DebugHistory(pContext, hopInternalReceiveResume, NULL, 0, 0, 0); } return status; } /************************************************************* Required NDIS procedure, spawns regular (Common) DPC processing *************************************************************/ VOID NTAPI ParaNdis5_HandleDPC(IN NDIS_HANDLE MiniportAdapterContext) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext; ULONG requiresProcessing; BOOLEAN unused; DEBUG_ENTRY(7); // we do not need the timer, as DPC will do all the job // this is not a problem if the timer procedure is already running, // we need to do our job anyway NdisCancelTimer(&pContext->DPCPostProcessTimer, &unused); requiresProcessing = ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE); if (requiresProcessing) { // we need to request additional DPC InterlockedOr(&pContext->InterruptStatus, requiresProcessing); NdisSetTimer(&pContext->DPCPostProcessTimer, 10); } } BOOLEAN ParaNdis_SynchronizeWithInterrupt( PARANDIS_ADAPTER *pContext, ULONG messageId, tSynchronizedProcedure procedure, PVOID parameter) { tSynchronizedContext SyncContext; SyncContext.pContext = pContext; SyncContext.Parameter = parameter; return NdisMSynchronizeWithInterrupt(&pContext->Interrupt, procedure, &SyncContext); }