589 lines
15 KiB
C
589 lines
15 KiB
C
|
|
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
recv.c
|
|
|
|
Abstract:
|
|
|
|
NDIS send entry points and utility routines to handle receiving
|
|
data.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
alid 10/22/2001 modified for TunMp driver
|
|
arvindm 4/6/2000 Created
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#define __FILENUMBER 'VCER'
|
|
|
|
|
|
NTSTATUS
|
|
TunRead(
|
|
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;
|
|
PTUN_ADAPTER pAdapter;
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
pAdapter = pIrpSp->FileObject->FsContext;
|
|
|
|
DEBUGP(DL_LOUD, ("==>TunRead, pAdapter %p\n",
|
|
pAdapter));
|
|
do
|
|
{
|
|
//
|
|
// Validate!
|
|
//
|
|
if (pAdapter == NULL)
|
|
{
|
|
DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n",
|
|
pIrpSp->FileObject));
|
|
NtStatus = STATUS_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
|
|
TUN_STRUCT_ASSERT(pAdapter, mc);
|
|
|
|
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.
|
|
//
|
|
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;
|
|
}
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
|
|
if (TUN_TEST_FLAGS(pAdapter, TUN_ADAPTER_OFF))
|
|
{
|
|
DEBUGP(DL_WARN, ("TunRead, Adapter off. pAdapter %p\n",
|
|
pAdapter));
|
|
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
NtStatus = STATUS_INVALID_DEVICE_STATE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add this IRP to the list of pended Read IRPs
|
|
//
|
|
TUN_INSERT_TAIL_LIST(&pAdapter->PendedReads, &pIrp->Tail.Overlay.ListEntry);
|
|
TUN_REF_ADAPTER(pAdapter); // pended read IRP
|
|
pAdapter->PendedReadCount++;
|
|
|
|
//
|
|
// Set up the IRP for possible cancellation.
|
|
//
|
|
pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pAdapter;
|
|
IoMarkIrpPending(pIrp);
|
|
IoSetCancelRoutine(pIrp, TunCancelRead);
|
|
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
|
|
NtStatus = STATUS_PENDING;
|
|
|
|
//
|
|
// Run the service routine for reads.
|
|
//
|
|
TunServiceReads(pAdapter);
|
|
|
|
break;
|
|
}
|
|
while (FALSE);
|
|
|
|
if (NtStatus != STATUS_PENDING)
|
|
{
|
|
TUN_ASSERT(NtStatus != STATUS_SUCCESS);
|
|
pIrp->IoStatus.Information = 0;
|
|
pIrp->IoStatus.Status = NtStatus;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
DEBUGP(DL_LOUD, ("<==TunRead, pAdapter %p\n",
|
|
pAdapter));
|
|
|
|
return (NtStatus);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
TunCancelRead(
|
|
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
|
|
|
|
--*/
|
|
{
|
|
PTUN_ADAPTER pAdapter;
|
|
PLIST_ENTRY pEnt;
|
|
PLIST_ENTRY pIrpEntry;
|
|
BOOLEAN Found;
|
|
|
|
IoReleaseCancelSpinLock(pIrp->CancelIrql);
|
|
Found = FALSE;
|
|
pAdapter = (PTUN_ADAPTER) pIrp->Tail.Overlay.DriverContext[0];
|
|
|
|
DEBUGP(DL_LOUD, ("==>TunCancelRead, pAdapter %p\n",
|
|
pAdapter));
|
|
|
|
|
|
TUN_STRUCT_ASSERT(pAdapter, mc);
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
|
|
//
|
|
// Locate the IRP in the pended read queue and remove it if found.
|
|
//
|
|
for (pIrpEntry = pAdapter->PendedReads.Flink;
|
|
pIrpEntry != &pAdapter->PendedReads;
|
|
pIrpEntry = pIrpEntry->Flink)
|
|
{
|
|
if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry))
|
|
{
|
|
TUN_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry);
|
|
pAdapter->PendedReadCount--;
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((!TUN_TEST_FLAG(pAdapter, TUN_ADAPTER_ACTIVE)) &&
|
|
(pAdapter->PendedSendCount == 0) &&
|
|
(pAdapter->PendedReadCount == 0) &&
|
|
(TUN_TEST_FLAG(pAdapter, TUN_COMPLETE_REQUEST)))
|
|
{
|
|
TUN_CLEAR_FLAG(pAdapter, TUN_COMPLETE_REQUEST);
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
NdisMSetInformationComplete(&pAdapter->MiniportHandle,
|
|
NDIS_STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
}
|
|
|
|
if (Found)
|
|
{
|
|
DEBUGP(DL_LOUD, ("CancelRead: Open %p, IRP %p\n", pAdapter, pIrp));
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // Cancel removed pended Read
|
|
}
|
|
|
|
DEBUGP(DL_LOUD, ("<==TunCancelRead, pAdapter %p\n",
|
|
pAdapter));
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
TunServiceReads(
|
|
IN PTUN_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Utility routine to copy received data into user buffers and
|
|
complete READ IRPs.
|
|
|
|
Arguments:
|
|
|
|
pAdapter - 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, BytesCopied;
|
|
|
|
DEBUGP(DL_VERY_LOUD, ("==>ServiceReads: Adapter %p/%x\n",
|
|
pAdapter, pAdapter->Flags));
|
|
|
|
TUN_REF_ADAPTER(pAdapter);
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
|
|
while (!TUN_IS_LIST_EMPTY(&pAdapter->PendedReads) &&
|
|
!TUN_IS_LIST_EMPTY(&pAdapter->RecvPktQueue))
|
|
{
|
|
//
|
|
// Get the first pended Read IRP
|
|
//
|
|
pIrpEntry = pAdapter->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.
|
|
//
|
|
RemoveEntryList(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_LOUD, ("ServiceReads: Adapter %p, skipping cancelled IRP %p\n",
|
|
pAdapter, pIrp));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the first queued receive packet
|
|
//
|
|
pRcvPacketEntry = pAdapter->RecvPktQueue.Flink;
|
|
RemoveEntryList(pRcvPacketEntry);
|
|
|
|
pAdapter->RecvPktCount--;
|
|
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // Service: dequeue rcv packet
|
|
|
|
pRcvPacket = TUN_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
|
|
|
|
//
|
|
// Copy as much data as possible from the receive packet to
|
|
// the IRP MDL.
|
|
//
|
|
|
|
pDst = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
|
|
TUN_ASSERT(pDst != NULL); // since it was already mapped
|
|
BytesRemaining = MmGetMdlByteCount(pIrp->MdlAddress);
|
|
|
|
pNdisBuffer = pRcvPacket->Private.Head;
|
|
|
|
BytesCopied = 0;
|
|
|
|
while (BytesRemaining > 0 && (pNdisBuffer != NULL))
|
|
{
|
|
NdisQueryBufferSafe(pNdisBuffer, &pSrc, &BytesAvailable, NormalPagePriority);
|
|
|
|
if (pSrc == NULL)
|
|
{
|
|
DEBUGP(DL_FATAL,
|
|
("ServiceReads: Adapter %p, QueryBuffer failed for buffer %p\n",
|
|
pAdapter, pNdisBuffer));
|
|
break;
|
|
}
|
|
|
|
if (BytesAvailable)
|
|
{
|
|
ULONG BytesToCopy = MIN(BytesAvailable, BytesRemaining);
|
|
|
|
TUN_COPY_MEM(pDst, pSrc, BytesToCopy);
|
|
BytesCopied += BytesToCopy;
|
|
BytesRemaining -= BytesToCopy;
|
|
pDst += BytesToCopy;
|
|
}
|
|
|
|
NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer);
|
|
}
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
//1 shouldn't we fail the read IRP if we couldn't copy the entire data?
|
|
//1 check for pNdisBuffer != NULL
|
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining;
|
|
|
|
DEBUGP(DL_LOUD, ("ServiceReads: Adapter %p, IRP %p completed with %d bytes\n",
|
|
pAdapter, pIrp, pIrp->IoStatus.Information));
|
|
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
NdisMSendComplete(pAdapter->MiniportHandle,
|
|
pRcvPacket,
|
|
NDIS_STATUS_SUCCESS);
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // took out pended Read
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
pAdapter->PendedReadCount--;
|
|
pAdapter->SendPackets++;
|
|
pAdapter->SendBytes += BytesCopied;
|
|
|
|
|
|
}
|
|
|
|
//1 convert to macro or in-line function
|
|
if ((!TUN_TEST_FLAG(pAdapter, TUN_ADAPTER_ACTIVE)) &&
|
|
(pAdapter->PendedSendCount == 0) &&
|
|
(pAdapter->PendedReadCount == 0) &&
|
|
(TUN_TEST_FLAG(pAdapter, TUN_COMPLETE_REQUEST)))
|
|
{
|
|
TUN_CLEAR_FLAG(pAdapter, TUN_COMPLETE_REQUEST);
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
NdisMSetInformationComplete(&pAdapter->MiniportHandle,
|
|
NDIS_STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
}
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // temp ref - service reads
|
|
|
|
DEBUGP(DL_VERY_LOUD, ("<==ServiceReads: Adapter %p\n",
|
|
pAdapter));
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
TunCancelPendingReads(
|
|
IN PTUN_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancel any pending read IRPs queued on the given open.
|
|
|
|
Arguments:
|
|
|
|
pAdapter - pointer to open context
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PIRP pIrp;
|
|
PLIST_ENTRY pIrpEntry;
|
|
|
|
DEBUGP(DL_LOUD, ("==>TunCancelPendingReads: Adapter %p/%x\n",
|
|
pAdapter, pAdapter->Flags));
|
|
|
|
|
|
TUN_REF_ADAPTER(pAdapter); // temp ref - cancel reads
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
|
|
while (!TUN_IS_LIST_EMPTY(&pAdapter->PendedReads))
|
|
{
|
|
//
|
|
// Get the first pended Read IRP
|
|
//
|
|
pIrpEntry = pAdapter->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.
|
|
//
|
|
TUN_REMOVE_ENTRY_LIST(pIrpEntry);
|
|
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
|
|
//
|
|
// Complete the IRP.
|
|
//
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
DEBUGP(DL_LOUD, ("CancelPendingReads: Open %p, IRP %p cancelled\n",
|
|
pAdapter, pIrp));
|
|
|
|
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // took out pended Read for cancelling
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
pAdapter->PendedReadCount--;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It is being cancelled, let the cancel routine handle it.
|
|
//
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
|
|
//
|
|
// Give the cancel routine some breathing space, otherwise
|
|
// we might end up examining the same (cancelled) IRP over
|
|
// and over again.
|
|
//
|
|
TUN_SLEEP(1);
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
}
|
|
}
|
|
|
|
if ((!TUN_TEST_FLAG(pAdapter, TUN_ADAPTER_ACTIVE)) &&
|
|
(pAdapter->PendedSendCount == 0) &&
|
|
(pAdapter->PendedReadCount == 0) &&
|
|
(TUN_TEST_FLAG(pAdapter, TUN_COMPLETE_REQUEST)))
|
|
{
|
|
TUN_CLEAR_FLAG(pAdapter, TUN_COMPLETE_REQUEST);
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
NdisMSetInformationComplete(&pAdapter->MiniportHandle,
|
|
NDIS_STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
}
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // temp ref - cancel reads
|
|
|
|
DEBUGP(DL_LOUD, ("<==TunCancelPendingReads: Adapter %p/%x\n",
|
|
pAdapter, pAdapter->Flags));
|
|
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
TunFlushReceiveQueue(
|
|
IN PTUN_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free any receive packets queued up on the specified open
|
|
|
|
Arguments:
|
|
|
|
pAdapter - pointer to open context
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pRcvPacketEntry;
|
|
PNDIS_PACKET pRcvPacket;
|
|
|
|
DEBUGP(DL_LOUD, ("==>TunFlushReceiveQueue: Adapter %p/%x\n",
|
|
pAdapter, pAdapter->Flags));
|
|
|
|
TUN_REF_ADAPTER(pAdapter); // temp ref - flushRcvQueue
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
|
|
while (!TUN_IS_LIST_EMPTY(&pAdapter->RecvPktQueue))
|
|
{
|
|
//
|
|
// Get the first queued receive packet
|
|
//
|
|
pRcvPacketEntry = pAdapter->RecvPktQueue.Flink;
|
|
TUN_REMOVE_ENTRY_LIST(pRcvPacketEntry);
|
|
|
|
pAdapter->RecvPktCount --;
|
|
pAdapter->XmitError++;
|
|
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
|
|
pRcvPacket = TUN_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
|
|
|
|
DEBUGP(DL_LOUD, ("FlushReceiveQueue: open %p, pkt %p\n",
|
|
pAdapter, pRcvPacket));
|
|
|
|
NdisMSendComplete(pAdapter->MiniportHandle,
|
|
pRcvPacket,
|
|
NDIS_STATUS_REQUEST_ABORTED);
|
|
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // took out pended Read
|
|
|
|
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
|
|
}
|
|
|
|
TUN_RELEASE_LOCK(&pAdapter->Lock);
|
|
|
|
TUN_DEREF_ADAPTER(pAdapter); // temp ref - flushRcvQueue
|
|
|
|
DEBUGP(DL_LOUD, ("<==TunFlushReceiveQueue: Adapter %p/%x\n",
|
|
pAdapter, pAdapter->Flags));
|
|
|
|
}
|
|
|