/*++ 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)); }