NTSTATUS NICStartRecv( IN PFDO_DATA FdoData ) /*++ Routine Description: Start the receive unit if it's not in a ready state Assumption: This function is called with the Rcv SPINLOCK held. Arguments: FdoData Pointer to our FdoData Return Value: NT Status code --*/ { PMP_RFD pMpRfd; NTSTATUS status = STATUS_SUCCESS; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICStartRecv\n"); ASSERT(!IsListEmpty(&FdoData->RecvList)); // // Get the MP_RFD head // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); NICHandleRecvInterrupt(FdoData); ASSERT(!IsListEmpty(&FdoData->RecvList)); // // Get the new MP_RFD head // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); exit: TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICStartRecv, Status=%x\n", status); return status; }
VOID NICResetRecv( IN PFDO_DATA FdoData ) /*++ Routine Description: Reset the receive list Assumption: This function is called with the Rcv SPINLOCK held. Arguments: FdoData Pointer to our FdoData Return Value: None --*/ { PMP_RFD pMpRfd; PHW_RFD pHwRfd; ULONG RfdCount; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> NICResetRecv\n"); ASSERT(!IsListEmpty(&FdoData->RecvList)); // // Get the MP_RFD head // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); for (RfdCount = 0; RfdCount < FdoData->nReadyRecv; RfdCount++) { pHwRfd = pMpRfd->HwRfd; pHwRfd->RfdCbHeader.CbStatus = 0; pMpRfd = (PMP_RFD)GetListFLink(&pMpRfd->List); } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- NICResetRecv\n"); }
NTSTATUS NICStartRecv( IN PFDO_DATA FdoData ) /*++ Routine Description: Start the receive unit if it's not in a ready state Assumption: This function is called with the Rcv SPINLOCK held. Arguments: FdoData Pointer to our FdoData Return Value: NT Status code --*/ { PMP_RFD pMpRfd; NTSTATUS status; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICStartRecv\n"); // // If the receiver is ready, then don't try to restart. // if (NIC_IS_RECV_READY(FdoData)) { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Receive unit already active\n"); return STATUS_SUCCESS; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Re-start receive unit...\n"); ASSERT(!IsListEmpty(&FdoData->RecvList)); // // Get the MP_RFD head // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); // // If more packets are received, clean up RFD chain again // if (NIC_RFD_GET_STATUS(pMpRfd->HwRfd)) { NICHandleRecvInterrupt(FdoData); ASSERT(!IsListEmpty(&FdoData->RecvList)); // // Get the new MP_RFD head // pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); } // // Wait for the SCB to clear before we set the general pointer // if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; goto exit; } if (FdoData->DevicePowerState > PowerDeviceD0) { status = STATUS_DEVICE_DATA_ERROR; goto exit; } // // Set the SCB General Pointer to point the current Rfd // FdoData->CSRAddress->ScbGeneralPointer = pMpRfd->HwRfdPhys; // // Issue the SCB RU start command // status = D100IssueScbCommand(FdoData, SCB_RUC_START, FALSE); if (status == STATUS_SUCCESS) { // wait for the command to be accepted if (!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; } } exit: TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICStartRecv, Status=%x\n", status); return status; }
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 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"); }