windows-nt/Source/XPSP1/NT/net/ndis/uio/sys/recv.c
2020-09-26 16:20:57 +08:00

1250 lines
32 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
recv.c
Abstract:
NDIS protocol entry points and utility routines to handle receiving
data.
Environment:
Kernel mode only.
Revision History:
arvindm 4/6/2000 Created
--*/
#include "precomp.h"
#define __FILENUMBER 'VCER'
NTSTATUS
NdisuioRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Dispatch routine to handle IRP_MJ_READ.
Arguments:
pDeviceObject - pointer to our device object
pIrp - Pointer to request packet
Return Value:
NT status code.
--*/
{
PIO_STACK_LOCATION pIrpSp;
ULONG FunctionCode;
NTSTATUS NtStatus;
PNDISUIO_OPEN_CONTEXT pOpenContext;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pOpenContext = pIrpSp->FileObject->FsContext;
do
{
//
// Validate!
//
if (pOpenContext == NULL)
{
DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n",
pIrpSp->FileObject));
NtStatus = STATUS_INVALID_HANDLE;
break;
}
NUIO_STRUCT_ASSERT(pOpenContext, oc);
if (pIrp->MdlAddress == NULL)
{
DEBUGP(DL_FATAL, ("Read: NULL MDL address on IRP %p\n", pIrp));
NtStatus = STATUS_INVALID_PARAMETER;
break;
}
//
// Try to get a virtual address for the MDL.
//
#ifndef WIN9X
if (MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority) == NULL)
{
DEBUGP(DL_FATAL, ("Read: MmGetSystemAddr failed for IRP %p, MDL %p\n",
pIrp, pIrp->MdlAddress));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
#endif
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
if (!NUIO_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE))
{
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NtStatus = STATUS_INVALID_HANDLE;
break;
}
//
// Add this IRP to the list of pended Read IRPs
//
NUIO_INSERT_TAIL_LIST(&pOpenContext->PendedReads, &pIrp->Tail.Overlay.ListEntry);
NUIO_REF_OPEN(pOpenContext); // pended read IRP
pOpenContext->PendedReadCount++;
//
// Set up the IRP for possible cancellation.
//
pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext;
IoMarkIrpPending(pIrp);
IoSetCancelRoutine(pIrp, NdisuioCancelRead);
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NtStatus = STATUS_PENDING;
//
// Run the service routine for reads.
//
ndisuioServiceReads(pOpenContext);
break;
}
while (FALSE);
if (NtStatus != STATUS_PENDING)
{
NUIO_ASSERT(NtStatus != STATUS_SUCCESS);
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
return (NtStatus);
}
VOID
NdisuioCancelRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Cancel a pending read IRP. We unlink the IRP from the open context
queue and complete it.
Arguments:
pDeviceObject - pointer to our device object
pIrp - IRP to be cancelled
Return Value:
None
--*/
{
PNDISUIO_OPEN_CONTEXT pOpenContext;
PLIST_ENTRY pEnt;
PLIST_ENTRY pIrpEntry;
BOOLEAN Found;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
Found = FALSE;
pOpenContext = (PNDISUIO_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0];
NUIO_STRUCT_ASSERT(pOpenContext, oc);
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
//
// Locate the IRP in the pended read queue and remove it if found.
//
for (pIrpEntry = pOpenContext->PendedReads.Flink;
pIrpEntry != &pOpenContext->PendedReads;
pIrpEntry = pIrpEntry->Flink)
{
if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry))
{
NUIO_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry);
pOpenContext->PendedReadCount--;
Found = TRUE;
break;
}
}
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
if (Found)
{
DEBUGP(DL_INFO, ("CancelRead: Open %p, IRP %p\n", pOpenContext, pIrp));
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
NUIO_DEREF_OPEN(pOpenContext); // Cancel removed pended Read
}
}
VOID
ndisuioServiceReads(
IN PNDISUIO_OPEN_CONTEXT pOpenContext
)
/*++
Routine Description:
Utility routine to copy received data into user buffers and
complete READ IRPs.
Arguments:
pOpenContext - pointer to open context
Return Value:
None
--*/
{
PIRP pIrp;
PLIST_ENTRY pIrpEntry;
PNDIS_PACKET pRcvPacket;
PLIST_ENTRY pRcvPacketEntry;
PUCHAR pSrc, pDst;
ULONG BytesRemaining; // at pDst
PNDIS_BUFFER pNdisBuffer;
ULONG BytesAvailable;
DEBUGP(DL_VERY_LOUD, ("ServiceReads: open %p/%x\n",
pOpenContext, pOpenContext->Flags));
NUIO_REF_OPEN(pOpenContext); // temp ref - service reads
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
while (!NUIO_IS_LIST_EMPTY(&pOpenContext->PendedReads) &&
!NUIO_IS_LIST_EMPTY(&pOpenContext->RecvPktQueue))
{
//
// Get the first pended Read IRP
//
pIrpEntry = pOpenContext->PendedReads.Flink;
pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry);
//
// Check to see if it is being cancelled.
//
if (IoSetCancelRoutine(pIrp, NULL))
{
//
// It isn't being cancelled, and can't be cancelled henceforth.
//
NUIO_REMOVE_ENTRY_LIST(pIrpEntry);
//
// NOTE: we decrement PendedReadCount way below in the
// while loop, to avoid letting through a thread trying
// to unbind.
//
}
else
{
//
// The IRP is being cancelled; let the cancel routine handle it.
//
DEBUGP(DL_INFO, ("ServiceReads: open %p, skipping cancelled IRP %p\n",
pOpenContext, pIrp));
continue;
}
//
// Get the first queued receive packet
//
pRcvPacketEntry = pOpenContext->RecvPktQueue.Flink;
NUIO_REMOVE_ENTRY_LIST(pRcvPacketEntry);
pOpenContext->RecvPktCount --;
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NUIO_DEREF_OPEN(pOpenContext); // Service: dequeue rcv packet
pRcvPacket = NUIO_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
//
// Copy as much data as possible from the receive packet to
// the IRP MDL.
//
#ifndef WIN9X
pDst = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
NUIO_ASSERT(pDst != NULL); // since it was already mapped
#else
pDst = MmGetSystemAddressForMdl(pIrp->MdlAddress); // Win9x
#endif
BytesRemaining = MmGetMdlByteCount(pIrp->MdlAddress);
pNdisBuffer = pRcvPacket->Private.Head;
while (BytesRemaining && (pNdisBuffer != NULL))
{
#ifndef WIN9X
NdisQueryBufferSafe(pNdisBuffer, &pSrc, &BytesAvailable, NormalPagePriority);
if (pSrc == NULL)
{
DEBUGP(DL_FATAL,
("ServiceReads: Open %p, QueryBuffer failed for buffer %p\n",
pOpenContext, pNdisBuffer));
break;
}
#else
NdisQueryBuffer(pNdisBuffer, &pSrc, &BytesAvailable);
#endif
if (BytesAvailable)
{
ULONG BytesToCopy = MIN(BytesAvailable, BytesRemaining);
NUIO_COPY_MEM(pDst, pSrc, BytesToCopy);
BytesRemaining -= BytesToCopy;
pDst += BytesToCopy;
}
NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer);
}
//
// Complete the IRP.
//
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining;
DEBUGP(DL_INFO, ("ServiceReads: Open %p, IRP %p completed with %d bytes\n",
pOpenContext, pIrp, pIrp->IoStatus.Information));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
//
// Free up the receive packet - back to the miniport if it
// belongs to it, else reclaim it (local copy).
//
if (NdisGetPoolFromPacket(pRcvPacket) != pOpenContext->RecvPacketPool)
{
NdisReturnPackets(&pRcvPacket, 1);
}
else
{
ndisuioFreeReceivePacket(pOpenContext, pRcvPacket);
}
NUIO_DEREF_OPEN(pOpenContext); // took out pended Read
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
pOpenContext->PendedReadCount--;
}
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NUIO_DEREF_OPEN(pOpenContext); // temp ref - service reads
}
NDIS_STATUS
NdisuioReceive(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE MacReceiveContext,
IN PVOID pHeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID pLookaheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
)
/*++
Routine Description:
Our protocol receive handler called by NDIS, typically if we have
a miniport below that doesn't indicate packets.
We make a local packet/buffer copy of this data, queue it up, and
kick off the read service routine.
Arguments:
ProtocolBindingContext - pointer to open context
MacReceiveContext - for use in NdisTransferData
pHeaderBuffer - pointer to data header
HeaderBufferSize - size of the above
pLookaheadBuffer - pointer to buffer containing lookahead data
LookaheadBufferSize - size of the above
PacketSize - size of the entire packet, minus header size.
Return Value:
NDIS_STATUS_NOT_ACCEPTED - if this packet is uninteresting
NDIS_STATUS_SUCCESS - if we processed this successfully
--*/
{
PNDISUIO_OPEN_CONTEXT pOpenContext;
NDIS_STATUS Status;
PNDISUIO_ETH_HEADER pEthHeader;
PNDIS_PACKET pRcvPacket;
PUCHAR pRcvData;
UINT BytesTransferred;
PNDIS_BUFFER pOriginalNdisBuffer, pPartialNdisBuffer;
pOpenContext = (PNDISUIO_OPEN_CONTEXT)ProtocolBindingContext;
NUIO_STRUCT_ASSERT(pOpenContext, oc);
pRcvPacket = NULL;
pRcvData = NULL;
Status = NDIS_STATUS_SUCCESS;
do
{
if (HeaderBufferSize != sizeof(NDISUIO_ETH_HEADER))
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
pEthHeader = (PNDISUIO_ETH_HEADER)pHeaderBuffer;
//
// Check the EtherType. If the Ether type indicates presence of
// a tag, then the "real" Ether type is 4 bytes further down.
//
if (pEthHeader->EthType == NUIO_8021P_TAG_TYPE)
{
USHORT UNALIGNED *pEthType;
pEthType = (USHORT UNALIGNED *)((PUCHAR)&pEthHeader->EthType + 4);
if (*pEthType != Globals.EthType)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
}
else if (pEthHeader->EthType != Globals.EthType)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
//
// Allocate resources for queueing this up.
//
pRcvPacket = ndisuioAllocateReceivePacket(
pOpenContext,
PacketSize + HeaderBufferSize,
&pRcvData
);
if (pRcvPacket == NULL)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
NdisMoveMappedMemory(pRcvData, pHeaderBuffer, HeaderBufferSize);
//
// Check if the entire packet is within the lookahead.
//
if (PacketSize == LookaheadBufferSize)
{
NdisCopyLookaheadData(pRcvData+HeaderBufferSize,
pLookaheadBuffer,
LookaheadBufferSize,
pOpenContext->MacOptions);
//
// Queue this up for receive processing, and
// try to complete some read IRPs.
//
ndisuioQueueReceivePacket(pOpenContext, pRcvPacket);
}
else
{
//
// Allocate an NDIS buffer to map the receive area
// at an offset "HeaderBufferSize" from the current
// start. This is so that NdisTransferData can copy
// in at the right point in the destination buffer.
//
NdisAllocateBuffer(
&Status,
&pPartialNdisBuffer,
pOpenContext->RecvBufferPool,
pRcvData + HeaderBufferSize,
PacketSize);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Unlink and save away the original NDIS Buffer
// that maps the full receive buffer.
//
NdisUnchainBufferAtFront(pRcvPacket, &pOriginalNdisBuffer);
NUIO_RCV_PKT_TO_ORIGINAL_BUFFER(pRcvPacket) = pOriginalNdisBuffer;
//
// Link in the partial buffer for NdisTransferData to
// operate on.
//
NdisChainBufferAtBack(pRcvPacket, pPartialNdisBuffer);
DEBUGP(DL_LOUD, ("Receive: setting up for TransferData:"
" Pkt %p, OriginalBuf %p, PartialBuf %p\n",
pRcvPacket, pOriginalNdisBuffer, pPartialNdisBuffer));
NdisTransferData(
&Status,
pOpenContext->BindingHandle,
MacReceiveContext,
0, // ByteOffset
PacketSize,
pRcvPacket,
&BytesTransferred);
}
else
{
//
// Failure handled below in TransferDataComplete.
//
BytesTransferred = 0;
}
if (Status != NDIS_STATUS_PENDING)
{
NdisuioTransferDataComplete(
(NDIS_HANDLE)pOpenContext,
pRcvPacket,
Status,
BytesTransferred);
}
}
break;
}
while (FALSE);
return Status;
DEBUGP(DL_LOUD, ("Receive: Open %p, Pkt %p, Size %d\n",
pOpenContext, pRcvPacket, PacketSize));
}
VOID
NdisuioTransferDataComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pNdisPacket,
IN NDIS_STATUS TransferStatus,
IN UINT BytesTransferred
)
/*++
Routine Description:
NDIS entry point called to signal completion of a call to
NdisTransferData that had pended.
Arguments:
ProtocolBindingContext - pointer to open context
pNdisPacket - our receive packet into which data is transferred
TransferStatus - status of the transfer
BytesTransferred - bytes copied into the packet.
Return Value:
None
--*/
{
PNDISUIO_OPEN_CONTEXT pOpenContext;
PNDIS_BUFFER pOriginalBuffer, pPartialBuffer;
pOpenContext = (PNDISUIO_OPEN_CONTEXT)ProtocolBindingContext;
NUIO_STRUCT_ASSERT(pOpenContext, oc);
//
// Check if an NDIS_BUFFER was created to map part of the receive buffer;
// if so, free it and link back the original NDIS_BUFFER that maps
// the full receive buffer to the packet.
//
pOriginalBuffer = NUIO_RCV_PKT_TO_ORIGINAL_BUFFER(pNdisPacket);
if (pOriginalBuffer != NULL)
{
//
// We had stashed off the NDIS_BUFFER for the full receive
// buffer in the packet reserved area. Unlink the partial
// buffer and link in the full buffer.
//
NdisUnchainBufferAtFront(pNdisPacket, &pPartialBuffer);
NdisChainBufferAtBack(pNdisPacket, pOriginalBuffer);
DEBUGP(DL_LOUD, ("TransferComp: Pkt %p, OrigBuf %p, PartialBuf %p\n",
pNdisPacket, pOriginalBuffer, pPartialBuffer));
//
// Free up the partial buffer.
//
NdisFreeBuffer(pPartialBuffer);
}
if (TransferStatus == NDIS_STATUS_SUCCESS)
{
//
// Queue this up for receive processing, and
// try to complete some read IRPs.
//
ndisuioQueueReceivePacket(pOpenContext, pNdisPacket);
}
else
{
ndisuioFreeReceivePacket(pOpenContext, pNdisPacket);
}
}
VOID
NdisuioReceiveComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
/*++
Routine Description:
Protocol entry point called by NDIS when the miniport
has finished indicating up a batch of receives.
We ignore this.
Arguments:
ProtocolBindingContext - pointer to open context
Return Value:
None
--*/
{
PNDISUIO_OPEN_CONTEXT pOpenContext;
pOpenContext = (PNDISUIO_OPEN_CONTEXT)ProtocolBindingContext;
NUIO_STRUCT_ASSERT(pOpenContext, oc);
return;
}
INT
NdisuioReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pNdisPacket
)
/*++
Routine Description:
Protocol entry point called by NDIS if the driver below
uses NDIS 4 style receive packet indications.
If the miniport allows us to hold on to this packet, we
use it as is, otherwise we make a copy.
Arguments:
ProtocolBindingContext - pointer to open context
pNdisPacket - the packet being indicated up.
Return Value:
None
--*/
{
PNDISUIO_OPEN_CONTEXT pOpenContext;
PNDIS_BUFFER pNdisBuffer;
UINT BufferLength;
PNDISUIO_ETH_HEADER pEthHeader;
PNDIS_PACKET pCopyPacket;
PUCHAR pCopyBuf;
UINT TotalPacketLength;
UINT BytesCopied;
INT RefCount = 0;
NDIS_STATUS Status;
pOpenContext = (PNDISUIO_OPEN_CONTEXT)ProtocolBindingContext;
NUIO_STRUCT_ASSERT(pOpenContext, oc);
#ifdef NDIS51
NdisGetFirstBufferFromPacketSafe(
pNdisPacket,
&pNdisBuffer,
&pEthHeader,
&BufferLength,
&TotalPacketLength,
NormalPagePriority);
if (pEthHeader == NULL)
{
//
// The system is low on resources. Set up to handle failure
// below.
//
BufferLength = 0;
}
#else
NdisGetFirstBufferFromPacket(
pNdisPacket,
&pNdisBuffer,
&pEthHeader,
&BufferLength,
&TotalPacketLength);
#endif
do
{
if (BufferLength < sizeof(NDISUIO_ETH_HEADER))
{
DEBUGP(DL_WARN,
("ReceivePacket: Open %p, runt pkt %p, first buffer length %d\n",
pOpenContext, pNdisPacket, BufferLength));
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
//
// Check the EtherType. If the Ether type indicates presence of
// a tag, then the "real" Ether type is 4 bytes further down.
//
if (pEthHeader->EthType == NUIO_8021P_TAG_TYPE)
{
USHORT UNALIGNED *pEthType;
pEthType = (USHORT UNALIGNED *)((PUCHAR)&pEthHeader->EthType + 4);
if (*pEthType != Globals.EthType)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
}
else if (pEthHeader->EthType != Globals.EthType)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
DEBUGP(DL_LOUD, ("ReceivePacket: Open %p, interesting pkt %p\n",
pOpenContext, pNdisPacket));
//
// If the miniport is out of resources, we can't queue
// this packet - make a copy if this is so.
//
if ((NDIS_GET_PACKET_STATUS(pNdisPacket) == NDIS_STATUS_RESOURCES) ||
pOpenContext->bRunningOnWin9x)
{
pCopyPacket = ndisuioAllocateReceivePacket(
pOpenContext,
TotalPacketLength,
&pCopyBuf
);
if (pCopyPacket == NULL)
{
DEBUGP(DL_FATAL, ("ReceivePacket: Open %p, failed to"
" alloc copy, %d bytes\n", pOpenContext, TotalPacketLength));
break;
}
NdisCopyFromPacketToPacket(
pCopyPacket,
0,
TotalPacketLength,
pNdisPacket,
0,
&BytesCopied);
NUIO_ASSERT(BytesCopied == TotalPacketLength);
pNdisPacket = pCopyPacket;
}
else
{
//
// We can queue the original packet - return
// a packet reference count indicating that
// we will call NdisReturnPackets when we are
// done with this packet.
//
RefCount = 1;
}
//
// Queue this up and service any pending Read IRPs.
//
ndisuioQueueReceivePacket(pOpenContext, pNdisPacket);
break;
}
while (FALSE);
return (RefCount);
}
VOID
ndisuioQueueReceivePacket(
IN PNDISUIO_OPEN_CONTEXT pOpenContext,
IN PNDIS_PACKET pRcvPacket
)
/*++
Routine Description:
Queue up a received packet on the open context structure.
If the queue size goes beyond a water mark, discard a packet
at the head of the queue.
Finally, run the queue service routine.
Arguments:
pOpenContext - pointer to open context
pRcvPacket - the received packet
Return Value:
None
--*/
{
PLIST_ENTRY pEnt;
PLIST_ENTRY pDiscardEnt;
PNDIS_PACKET pDiscardPkt;
do
{
pEnt = NUIO_RCV_PKT_TO_LIST_ENTRY(pRcvPacket);
NUIO_REF_OPEN(pOpenContext); // queued rcv packet
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
//
// Check if the binding is in the proper state to receive
// this packet.
//
if (NUIO_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE) &&
(pOpenContext->PowerState == NetDeviceStateD0))
{
NUIO_INSERT_TAIL_LIST(&pOpenContext->RecvPktQueue, pEnt);
pOpenContext->RecvPktCount++;
DEBUGP(DL_VERY_LOUD, ("QueueReceivePacket: open %p,"
" queued pkt %p, queue size %d\n",
pOpenContext, pRcvPacket, pOpenContext->RecvPktCount));
}
else
{
//
// Received this packet when the binding is going away.
// Drop this.
//
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
ndisuioFreeReceivePacket(pOpenContext, pRcvPacket);
NUIO_DEREF_OPEN(pOpenContext); // dropped rcv packet - bad state
break;
}
//
// Trim the queue if it has grown too big.
//
if (pOpenContext->RecvPktCount > MAX_RECV_QUEUE_SIZE)
{
//
// Remove the head of the queue.
//
pDiscardEnt = pOpenContext->RecvPktQueue.Flink;
NUIO_REMOVE_ENTRY_LIST(pDiscardEnt);
pOpenContext->RecvPktCount --;
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
pDiscardPkt = NUIO_LIST_ENTRY_TO_RCV_PKT(pDiscardEnt);
ndisuioFreeReceivePacket(pOpenContext, pDiscardPkt);
NUIO_DEREF_OPEN(pOpenContext); // dropped rcv packet - queue too long
DEBUGP(DL_INFO, ("QueueReceivePacket: open %p queue"
" too long, discarded pkt %p\n",
pOpenContext, pDiscardPkt));
}
else
{
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
}
//
// Run the receive queue service routine now.
//
ndisuioServiceReads(pOpenContext);
}
while (FALSE);
}
PNDIS_PACKET
ndisuioAllocateReceivePacket(
IN PNDISUIO_OPEN_CONTEXT pOpenContext,
IN UINT DataLength,
OUT PUCHAR * ppDataBuffer
)
/*++
Routine Description:
Allocate resources to copy and queue a received packet.
Arguments:
pOpenContext - pointer to open context for received packet
DataLength - total length in bytes of the packet
ppDataBuffer - place to return pointer to allocated buffer
Return Value:
Pointer to NDIS packet if successful, else NULL.
--*/
{
PNDIS_PACKET pNdisPacket;
PNDIS_BUFFER pNdisBuffer;
PUCHAR pDataBuffer;
NDIS_STATUS Status;
pNdisPacket = NULL;
pNdisBuffer = NULL;
pDataBuffer = NULL;
do
{
NUIO_ALLOC_MEM(pDataBuffer, DataLength);
if (pDataBuffer == NULL)
{
DEBUGP(DL_FATAL, ("AllocRcvPkt: open %p, failed to alloc"
" data buffer %d bytes\n", pOpenContext, DataLength));
break;
}
//
// Make this an NDIS buffer.
//
NdisAllocateBuffer(
&Status,
&pNdisBuffer,
pOpenContext->RecvBufferPool,
pDataBuffer,
DataLength);
if (Status != NDIS_STATUS_SUCCESS)
{
DEBUGP(DL_FATAL, ("AllocateRcvPkt: open %p, failed to alloc"
" NDIS buffer, %d bytes\n", pOpenContext, DataLength));
break;
}
NdisAllocatePacket(&Status, &pNdisPacket, pOpenContext->RecvPacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
DEBUGP(DL_FATAL, ("AllocateRcvPkt: open %p, failed to alloc"
" NDIS packet, %d bytes\n", pOpenContext, DataLength));
break;
}
NDIS_SET_PACKET_STATUS(pNdisPacket, 0);
NUIO_RCV_PKT_TO_ORIGINAL_BUFFER(pNdisPacket) = NULL;
NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);
*ppDataBuffer = pDataBuffer;
break;
}
while (FALSE);
if (pNdisPacket == NULL)
{
//
// Clean up
//
if (pNdisBuffer != NULL)
{
NdisFreeBuffer(pNdisBuffer);
}
if (pDataBuffer != NULL)
{
NUIO_FREE_MEM(pDataBuffer);
}
}
return (pNdisPacket);
}
VOID
ndisuioFreeReceivePacket(
IN PNDISUIO_OPEN_CONTEXT pOpenContext,
IN PNDIS_PACKET pNdisPacket
)
/*++
Routine Description:
Free up all resources associated with a received packet. If this
is a local copy, free the packet to our receive pool, else return
this to the miniport.
Arguments:
pOpenContext - pointer to open context
pNdisPacket - pointer to packet to be freed.
Return Value:
None
--*/
{
PNDIS_BUFFER pNdisBuffer;
UINT TotalLength;
UINT BufferLength;
PUCHAR pCopyData;
if (NdisGetPoolFromPacket(pNdisPacket) == pOpenContext->RecvPacketPool)
{
//
// This is a local copy.
//
#ifdef NDIS51
NdisGetFirstBufferFromPacketSafe(
pNdisPacket,
&pNdisBuffer,
(PVOID *)&pCopyData,
&BufferLength,
&TotalLength,
NormalPagePriority);
#else
NdisGetFirstBufferFromPacket(
pNdisPacket,
&pNdisBuffer,
(PVOID *)&pCopyData,
&BufferLength,
&TotalLength);
#endif
NUIO_ASSERT(BufferLength == TotalLength);
NUIO_ASSERT(pNdisBuffer != NULL);
NUIO_ASSERT(pCopyData != NULL); // we would have allocated non-paged pool
NdisFreePacket(pNdisPacket);
NdisFreeBuffer(pNdisBuffer);
NUIO_FREE_MEM(pCopyData);
}
else
{
NdisReturnPackets(&pNdisPacket, 1);
}
}
VOID
ndisuioCancelPendingReads(
IN PNDISUIO_OPEN_CONTEXT pOpenContext
)
/*++
Routine Description:
Cancel any pending read IRPs queued on the given open.
Arguments:
pOpenContext - pointer to open context
Return Value:
None
--*/
{
PIRP pIrp;
PLIST_ENTRY pIrpEntry;
NUIO_REF_OPEN(pOpenContext); // temp ref - cancel reads
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
while (!NUIO_IS_LIST_EMPTY(&pOpenContext->PendedReads))
{
//
// Get the first pended Read IRP
//
pIrpEntry = pOpenContext->PendedReads.Flink;
pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry);
//
// Check to see if it is being cancelled.
//
if (IoSetCancelRoutine(pIrp, NULL))
{
//
// It isn't being cancelled, and can't be cancelled henceforth.
//
NUIO_REMOVE_ENTRY_LIST(pIrpEntry);
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
//
// Complete the IRP.
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
DEBUGP(DL_INFO, ("CancelPendingReads: Open %p, IRP %p cancelled\n",
pOpenContext, pIrp));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
NUIO_DEREF_OPEN(pOpenContext); // took out pended Read for cancelling
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
pOpenContext->PendedReadCount--;
}
else
{
//
// It is being cancelled, let the cancel routine handle it.
//
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
//
// Give the cancel routine some breathing space, otherwise
// we might end up examining the same (cancelled) IRP over
// and over again.
//
NUIO_SLEEP(1);
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
}
}
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NUIO_DEREF_OPEN(pOpenContext); // temp ref - cancel reads
}
VOID
ndisuioFlushReceiveQueue(
IN PNDISUIO_OPEN_CONTEXT pOpenContext
)
/*++
Routine Description:
Free any receive packets queued up on the specified open
Arguments:
pOpenContext - pointer to open context
Return Value:
None
--*/
{
PLIST_ENTRY pRcvPacketEntry;
PNDIS_PACKET pRcvPacket;
NUIO_REF_OPEN(pOpenContext); // temp ref - flushRcvQueue
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
while (!NUIO_IS_LIST_EMPTY(&pOpenContext->RecvPktQueue))
{
//
// Get the first queued receive packet
//
pRcvPacketEntry = pOpenContext->RecvPktQueue.Flink;
NUIO_REMOVE_ENTRY_LIST(pRcvPacketEntry);
pOpenContext->RecvPktCount --;
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
pRcvPacket = NUIO_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
DEBUGP(DL_LOUD, ("FlushReceiveQueue: open %p, pkt %p\n",
pOpenContext, pRcvPacket));
ndisuioFreeReceivePacket(pOpenContext, pRcvPacket);
NUIO_DEREF_OPEN(pOpenContext); // took out pended Read
NUIO_ACQUIRE_LOCK(&pOpenContext->Lock);
}
NUIO_RELEASE_LOCK(&pOpenContext->Lock);
NUIO_DEREF_OPEN(pOpenContext); // temp ref - flushRcvQueue
}