VOID NICHandleRecvInterrupt( IN PFDO_DATA FdoData ) /*++ Routine Description: Interrupt handler for receive processing. Put the received packets into an array and call NICServiceReadIrps. If we run low on RFDs, allocate another one. Assumption: This function is called with the Rcv SPINLOCK held. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { PMP_RFD pMpRfd = NULL; PHW_RFD pHwRfd = NULL; PMP_RFD PacketArray[NIC_DEF_RFDS]; PMP_RFD PacketFreeArray[NIC_DEF_RFDS]; UINT PacketArrayCount; UINT PacketFreeCount; UINT Index; UINT LoopIndex = 0; UINT LoopCount = NIC_MAX_RFDS / NIC_DEF_RFDS + 1; // avoid staying here too long BOOLEAN bContinue = TRUE; BOOLEAN bAllocNewRfd = FALSE; USHORT PacketStatus; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICHandleRecvInterrupt\n"); ASSERT(FdoData->nReadyRecv >= NIC_MIN_RFDS); while (LoopIndex++ < LoopCount && bContinue) { PacketArrayCount = 0; PacketFreeCount = 0; // // Process up to the array size RFD's // while (PacketArrayCount < NIC_DEF_RFDS) { if (IsListEmpty(&FdoData->RecvList)) { ASSERT(FdoData->nReadyRecv == 0); bContinue = FALSE; break; } // // Get the next MP_RFD to process // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); // // Get the associated HW_RFD // pHwRfd = pMpRfd->HwRfd; // // Is this packet completed? // PacketStatus = NIC_RFD_GET_STATUS(pHwRfd); if (!NIC_RFD_STATUS_COMPLETED(PacketStatus)) { bContinue = FALSE; break; } // // HW specific - check if actual count field has been updated // if (!NIC_RFD_VALID_ACTUALCOUNT(pHwRfd)) { bContinue = FALSE; break; } // // Remove the RFD from the head of the List // RemoveEntryList((PLIST_ENTRY)pMpRfd); FdoData->nReadyRecv--; ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_READY)); MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_READY); // // A good packet? drop it if not. // if (!NIC_RFD_STATUS_SUCCESS(PacketStatus)) { TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Receive failure = %x\n", PacketStatus); NICReturnRFD(FdoData, pMpRfd); continue; } // // Do not receive any packets until a filter has been set // if (!FdoData->PacketFilter) { NICReturnRFD(FdoData, pMpRfd); continue; } // // Do not receive any packets until we are at D0 // if (FdoData->DevicePowerState != PowerDeviceD0) { NICReturnRFD(FdoData, pMpRfd); continue; } pMpRfd->PacketSize = NIC_RFD_GET_PACKET_SIZE(pHwRfd); KeFlushIoBuffers(pMpRfd->Mdl, TRUE, TRUE); // // set the status on the packet, either resources or success // if (FdoData->nReadyRecv >= MIN_NUM_RFD) { MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_PEND); } else { MP_SET_FLAG(pMpRfd, fMP_RFD_RESOURCES); _Analysis_assume_(PacketFreeCount <= PacketArrayCount); PacketFreeArray[PacketFreeCount] = pMpRfd; PacketFreeCount++; // // Reset the RFD shrink count - don't attempt to shrink RFD // FdoData->RfdShrinkCount = 0; // // Remember to allocate a new RFD later // bAllocNewRfd = TRUE; } PacketArray[PacketArrayCount] = pMpRfd; PacketArrayCount++; } // // if we didn't process any receives, just return from here // if (PacketArrayCount == 0) { break; } WdfSpinLockRelease(FdoData->RcvLock); WdfSpinLockAcquire(FdoData->Lock); // // if we have a Recv interrupt and have reported a media disconnect status // time to indicate the new status // if (Disconnected == FdoData->MediaState) { TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Media state changed to Connected\n"); MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_NO_CABLE); FdoData->MediaState = Connected; WdfSpinLockRelease(FdoData->Lock); // // Indicate the media event // NICServiceIndicateStatusIrp(FdoData); } else { WdfSpinLockRelease(FdoData->Lock); } NICServiceReadIrps( FdoData, PacketArray, PacketArrayCount); WdfSpinLockAcquire(FdoData->RcvLock); // // Return all the RFDs to the pool. // for (Index = 0; Index < PacketFreeCount; Index++) { // // Get the MP_RFD saved in this packet, in NICAllocRfd // pMpRfd = PacketFreeArray[Index]; ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RESOURCES)); MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RESOURCES); NICReturnRFD(FdoData, pMpRfd); } } // // If we ran low on RFD's, we need to allocate a new RFD // if (bAllocNewRfd) { // // Allocate one more RFD only if it doesn't exceed the max RFD limit // if (FdoData->CurrNumRfd < FdoData->MaxNumRfd && !FdoData->AllocNewRfd) { NTSTATUS status; FdoData->AllocNewRfd = TRUE; // // Since we are running at DISPATCH_LEVEL, we will queue a workitem // to allocate RFD memory at PASSIVE_LEVEL. Note that // AllocateCommonBuffer and FreeCommonBuffer can be called only at // PASSIVE_LEVEL. // status = PciDrvQueuePassiveLevelCallback(FdoData, NICAllocRfdWorkItem, NULL, NULL); if(!NT_SUCCESS(status)){ FdoData->AllocNewRfd = FALSE; } } } ASSERT(FdoData->nReadyRecv >= NIC_MIN_RFDS); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICHandleRecvInterrupt\n"); }
VOID NICServiceReadIrps( PFDO_DATA FdoData, PMP_RFD *PacketArray, ULONG PacketArrayCount ) /*++ Routine Description: Copy the data from the recv buffers to pending read IRP buffers and complete the IRP. When used as network driver, copy operation can be avoided by devising a private interface between us and the NDIS-WDM filter and have the NDIS-WDM edge to indicate our buffers directly to NDIS. Called at DISPATCH_LEVEL. Take advantage of that fact while acquiring spinlocks. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { PMP_RFD pMpRfd = NULL; ULONG index; NTSTATUS status; PVOID buffer; WDFREQUEST request; size_t bufLength=0; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> NICServiceReadIrps\n"); for(index=0; index < PacketArrayCount; index++) { pMpRfd = PacketArray[index]; ASSERT(pMpRfd); status = WdfIoQueueRetrieveNextRequest( FdoData->PendingReadQueue, &request ); if(NT_SUCCESS(status)){ WDF_REQUEST_PARAMETERS params; ULONG length = 0; WDF_REQUEST_PARAMETERS_INIT(¶ms); WdfRequestGetParameters( request, ¶ms ); ASSERT(status == STATUS_SUCCESS); bufLength = params.Parameters.Read.Length; status = WdfRequestRetrieveOutputBuffer(request, bufLength, &buffer, &bufLength); if(NT_SUCCESS(status) ) { length = min((ULONG)bufLength, pMpRfd->PacketSize); RtlCopyMemory(buffer, pMpRfd->Buffer, length); Hexdump((TRACE_LEVEL_VERBOSE, DBG_READ, "Received Packet Data: %!HEXDUMP!\n", log_xstr(buffer, (USHORT)length))); FdoData->BytesReceived += length; } WdfRequestCompleteWithInformation(request, status, length); }else { ASSERTMSG("WdfIoQueueRetrieveNextRequest failed", (status == STATUS_NO_MORE_ENTRIES || status == STATUS_WDF_PAUSED)); } WdfSpinLockAcquire(FdoData->RcvLock); ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_PEND)); MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_PEND); if (FdoData->RfdShrinkCount < NIC_RFD_SHRINK_THRESHOLD) { NICReturnRFD(FdoData, pMpRfd); } else { ASSERT(FdoData->CurrNumRfd > FdoData->NumRfd); status = PciDrvQueuePassiveLevelCallback(FdoData, NICFreeRfdWorkItem, (PVOID)pMpRfd, NULL); if(NT_SUCCESS(status)){ FdoData->RfdShrinkCount = 0; FdoData->CurrNumRfd--; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Shrink... CurrNumRfd = %d\n", FdoData->CurrNumRfd); } else { // // We couldn't queue a workitem to free memory, so let us // put that back in the main pool and try again next time. // NICReturnRFD(FdoData, pMpRfd); } } WdfSpinLockRelease(FdoData->RcvLock); }// end of loop TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- NICServiceReadIrps\n"); return; }
VOID NICHandleRecvInterrupt( IN PFDO_DATA FdoData ) /*++ Routine Description: Interrupt handler for receive processing. Put the received packets into an array and call NICServiceReadIrps. If we run low on RFDs, allocate another one. Assumption: This function is called with the Rcv SPINLOCK held. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { PMP_RFD pMpRfd = NULL; PULONG pHwRfd = NULL; PMP_RFD PacketArray[NIC_DEF_RFDS]; PMP_RFD PacketFreeArray[NIC_DEF_RFDS]; UINT PacketArrayCount; UINT PacketFreeCount; UINT Index; UINT LoopIndex = 0; UINT LoopCount = NIC_MAX_RFDS / NIC_DEF_RFDS + 1; // avoid staying here too long BOOLEAN bContinue = TRUE; BOOLEAN bAllocNewRfd = FALSE; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICHandleRecvInterrupt\n"); ASSERT(FdoData->nReadyRecv >= NIC_MIN_RFDS); while (LoopIndex++ < LoopCount && bContinue) { PacketArrayCount = 0; PacketFreeCount = 0; // // Process up to the array size RFD's // while (PacketArrayCount < NIC_DEF_RFDS) { if (IsListEmpty(&FdoData->RecvList)) { ASSERT(FdoData->nReadyRecv == 0); bContinue = FALSE; break; } // // Get the next MP_RFD to process // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); // // Get the associated HW_RFD // pHwRfd = pMpRfd->HwRfd; // // Remove the RFD from the head of the List // RemoveEntryList((PLIST_ENTRY)pMpRfd); FdoData->nReadyRecv--; ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_READY)); MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_READY); pMpRfd->PacketSize = 4; KeFlushIoBuffers(pMpRfd->Mdl, TRUE, TRUE); MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_PEND); PacketArray[PacketArrayCount] = pMpRfd; PacketArrayCount++; } // // if we didn't process any receives, just return from here // if (PacketArrayCount == 0) { break; } WdfSpinLockRelease(FdoData->RcvLock); NICServiceReadIrps( FdoData, PacketArray, PacketArrayCount); WdfSpinLockAcquire(FdoData->RcvLock); // // Return all the RFDs to the pool. // for (Index = 0; Index < PacketFreeCount; Index++) { // // Get the MP_RFD saved in this packet, in NICAllocRfd // pMpRfd = PacketFreeArray[Index]; ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RESOURCES)); MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RESOURCES); NICReturnRFD(FdoData, pMpRfd); } } ASSERT(FdoData->nReadyRecv >= NIC_MIN_RFDS); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICHandleRecvInterrupt\n"); }