Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550/* * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Interrupt handling * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov <di.sean@protonmail.com> */
/* INCLUDES *******************************************************************/
#include "nvnet.h"
#define NDEBUG#include "debug.h"
/* FUNCTIONS ******************************************************************/
ULONGProcessTransmitDescriptorsLegacy( _In_ PNVNET_ADAPTER Adapter, _Inout_ PLIST_ENTRY SendReadyList){ PNVNET_TCB Tcb = Adapter->Send.LastTcb; ULONG TcbProcessed = 0;
while (Tcb != Adapter->Send.CurrentTcb) { NVNET_TBD Tbd = Tcb->Tbd; ULONG Flags = Tbd.x32->FlagsLength;
if (Flags & NV_TX_VALID) break;
NDIS_DbgPrint(MIN_TRACE, ("Packet transmitted (flags %lx)\n", Flags & FLAG_MASK_V1));
if (Flags & NV_TX_ERROR) { ++Adapter->Statistics.TransmitErrors;
if (Flags & NV_TX_UNDERFLOW) ++Adapter->Statistics.TransmitUnderrunErrors; if (Flags & NV_TX_LATECOLLISION) ++Adapter->Statistics.TransmitLateCollisions; if (Flags & NV_TX_CARRIERLOST) ++Adapter->Statistics.TransmitLostCarrierSense; if (Flags & NV_TX_RETRYERROR) { ++Adapter->Statistics.TransmitExcessiveCollisions;
if (!(Flags & NV_TX_RETRYCOUNT_MASK)) { NvNetBackoffReseed(Adapter); } } } else { ++Adapter->Statistics.TransmitOk;
if (Flags & NV_TX_DEFERRED) { ++Adapter->Statistics.TransmitDeferred; } if (!(Flags & NV_TX_RETRYCOUNT_MASK)) ++Adapter->Statistics.TransmitZeroRetry; else if ((Flags & NV_TX_RETRYCOUNT_MASK) == NV_TX_ONE_RETRY) ++Adapter->Statistics.TransmitOneRetry; }
InsertTailList(SendReadyList, PACKET_ENTRY(Tcb->Packet));
NV_RELEASE_TCB(Adapter, Tcb);
++TcbProcessed;
Tcb = NV_NEXT_TCB(Adapter, Tcb); }
Adapter->Send.LastTcb = Tcb;
return TcbProcessed;}
ULONGProcessTransmitDescriptors32( _In_ PNVNET_ADAPTER Adapter, _Inout_ PLIST_ENTRY SendReadyList){ PNVNET_TCB Tcb = Adapter->Send.LastTcb; ULONG TcbProcessed = 0;
while (Tcb != Adapter->Send.CurrentTcb) { NVNET_TBD Tbd = Tcb->Tbd; ULONG Flags = Tbd.x32->FlagsLength;
if (Flags & NV_TX_VALID) break;
NDIS_DbgPrint(MIN_TRACE, ("Packet transmitted (flags %lx)\n", Flags & FLAG_MASK_V2));
if (Flags & NV_TX2_ERROR) { if ((Flags & NV_TX2_RETRYERROR) && !(Flags & NV_TX2_RETRYCOUNT_MASK)) { if (Adapter->Features & DEV_HAS_GEAR_MODE) NvNetBackoffReseedEx(Adapter); else NvNetBackoffReseed(Adapter); } }
InsertTailList(SendReadyList, PACKET_ENTRY(Tcb->Packet));
NV_RELEASE_TCB(Adapter, Tcb);
++TcbProcessed;
Tcb = NV_NEXT_TCB(Adapter, Tcb); }
Adapter->Send.LastTcb = Tcb;
return TcbProcessed;}
ULONGProcessTransmitDescriptors64( _In_ PNVNET_ADAPTER Adapter, _Inout_ PLIST_ENTRY SendReadyList){ PNVNET_TCB Tcb = Adapter->Send.LastTcb; ULONG TcbProcessed = 0;
while (Tcb != Adapter->Send.CurrentTcb) { NVNET_TBD Tbd = Tcb->Tbd; ULONG Flags = Tbd.x64->FlagsLength;
if (Flags & NV_TX_VALID) break;
if (Adapter->Flags & NV_SEND_ERRATA_PRESENT) { PNVNET_TCB DeferredTcb;
--Adapter->Send.PacketsCount;
DeferredTcb = Adapter->Send.DeferredTcb;
if (DeferredTcb) { DeferredTcb->DeferredTbd.x64->FlagsLength |= NV_TX2_VALID;
++Adapter->Send.PacketsCount;
Adapter->Send.DeferredTcb = NV_NEXT_TCB(Adapter, DeferredTcb); if (Adapter->Send.DeferredTcb == Adapter->Send.CurrentTcb) { Adapter->Send.DeferredTcb = NULL; }
NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl | NVREG_TXRXCTL_KICK); } }
NDIS_DbgPrint(MIN_TRACE, ("Packet transmitted (flags %lx)\n", Flags & FLAG_MASK_V2));
if (Flags & NV_TX2_ERROR) { if ((Flags & NV_TX2_RETRYERROR) && !(Flags & NV_TX2_RETRYCOUNT_MASK)) { if (Adapter->Features & DEV_HAS_GEAR_MODE) NvNetBackoffReseedEx(Adapter); else NvNetBackoffReseed(Adapter); } }
InsertTailList(SendReadyList, PACKET_ENTRY(Tcb->Packet));
NV_RELEASE_TCB(Adapter, Tcb);
++TcbProcessed;
Tcb = NV_NEXT_TCB(Adapter, Tcb); }
Adapter->Send.LastTcb = Tcb;
return TcbProcessed;}
staticBOOLEANHandleLengthError( _In_ PVOID EthHeader, _Inout_ PUSHORT Length){ PUCHAR Buffer = EthHeader; ULONG i;
DBG_UNREFERENCED_LOCAL_VARIABLE(Buffer);
/* TODO */ NDIS_DbgPrint(MAX_TRACE, ("() Length error detected (%u): \n", *Length)); for (i = 0; i < *Length; ++i) { NDIS_DbgPrint(MAX_TRACE, ("%02x ", Buffer[i])); } NDIS_DbgPrint(MAX_TRACE, ("\n\n*** Please report it to the team! ***\n\n"));
return FALSE;}
/* TODO: This need to be rewritten. I leave it as-is for now */staticULONGProcessReceiveDescriptors( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG TotalRxProcessed){ ULONG i, RxProcessed = 0; BOOLEAN IndicateComplete = FALSE;
for (i = 0; i < NVNET_RECEIVE_DESCRIPTORS; ++i) { ULONG Flags; USHORT Length; PUCHAR EthHeader; NV_RBD NvRbd;
if (Adapter->Features & DEV_HAS_HIGH_DMA) { NvRbd.x64 = &Adapter->Receive.NvRbd.x64[Adapter->CurrentRx]; Flags = NvRbd.x64->FlagsLength; } else { NvRbd.x32 = &Adapter->Receive.NvRbd.x32[Adapter->CurrentRx]; Flags = NvRbd.x32->FlagsLength; }
if (Flags & NV_RX_AVAIL) break;
if (TotalRxProcessed + RxProcessed >= NVNET_RECEIVE_PROCESSING_LIMIT) break;
if (!Adapter->PacketFilter) goto NextDescriptor;
if (Adapter->Features & (DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC)) { if (!(Flags & NV_RX2_DESCRIPTORVALID)) goto NextDescriptor;
Length = Flags & LEN_MASK_V2; EthHeader = &Adapter->ReceiveBuffer[Adapter->CurrentRx * NVNET_RECEIVE_BUFFER_SIZE];
if (Flags & NV_RX2_ERROR) { if ((Flags & NV_RX2_ERROR_MASK) == NV_RX2_ERROR4) { if (!HandleLengthError(EthHeader, &Length)) goto NextDescriptor; } else if ((Flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) { if (Flags & NV_RX2_SUBTRACT1) --Length; } else { goto NextDescriptor; } }
NDIS_DbgPrint(MIN_TRACE, ("Packet %d received (length %d, flags %lx)\n", Adapter->CurrentRx, Length, Flags & FLAG_MASK_V2)); } else { if (!(Flags & NV_RX_DESCRIPTORVALID)) goto NextDescriptor;
Length = Flags & LEN_MASK_V1; EthHeader = &Adapter->ReceiveBuffer[Adapter->CurrentRx * NVNET_RECEIVE_BUFFER_SIZE];
if (Flags & NV_RX_ERROR) { if ((Flags & NV_RX_ERROR_MASK) == NV_RX_ERROR4) { if (!HandleLengthError(EthHeader, &Length)) goto NextDescriptor; } else if ((Flags & NV_RX_ERROR_MASK) == NV_RX_FRAMINGERR) { if (Flags & NV_RX_SUBTRACT1) --Length; } else { ++Adapter->Statistics.ReceiveErrors;
if (Flags & NV_RX_MISSEDFRAME) ++Adapter->Statistics.ReceiveNoBuffers; if (Flags & NV_RX_FRAMINGERR) ++Adapter->Statistics.ReceiveAlignmentErrors; if (Flags & NV_RX_OVERFLOW) ++Adapter->Statistics.ReceiveOverrunErrors; if (Flags & NV_RX_CRCERR) ++Adapter->Statistics.ReceiveCrcErrors;
goto NextDescriptor; } } ++Adapter->Statistics.ReceiveOk;
NDIS_DbgPrint(MIN_TRACE, ("Packet %d received (length %d, flags %lx)\n", Adapter->CurrentRx, Length, Flags & FLAG_MASK_V1)); }
NdisMEthIndicateReceive(Adapter->AdapterHandle, NULL, (PCHAR)EthHeader, sizeof(ETH_HEADER), EthHeader + sizeof(ETH_HEADER), Length - sizeof(ETH_HEADER), Length - sizeof(ETH_HEADER)); IndicateComplete = TRUE;
NextDescriptor: /* Invalidate the buffer length and release the descriptor */ if (Adapter->Features & DEV_HAS_HIGH_DMA) NvRbd.x64->FlagsLength = NV_RX2_AVAIL | NVNET_RECEIVE_BUFFER_SIZE; else NvRbd.x32->FlagsLength = NV_RX_AVAIL | NVNET_RECEIVE_BUFFER_SIZE;
Adapter->CurrentRx = (Adapter->CurrentRx + 1) % NVNET_RECEIVE_DESCRIPTORS; ++RxProcessed; }
if (IndicateComplete) { NdisMEthIndicateReceiveComplete(Adapter->AdapterHandle); }
return RxProcessed;}
staticinlineVOIDChangeInterruptMode( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG Workload){ if (Workload > NVNET_IM_THRESHOLD) { Adapter->InterruptIdleCount = 0;
/* High activity, polling based strategy */ Adapter->InterruptMask = NVREG_IRQMASK_CPU; } else { if (Adapter->InterruptIdleCount < NVNET_IM_MAX_IDLE) { ++Adapter->InterruptIdleCount; } else { /* Low activity, 1 interrupt per packet */ Adapter->InterruptMask = NVREG_IRQMASK_THROUGHPUT; } }}
staticVOIDHandleLinkStateChange( _In_ PNVNET_ADAPTER Adapter){ ULONG MiiStatus; BOOLEAN Connected, Report = FALSE;
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
NdisDprAcquireSpinLock(&Adapter->Lock);
MiiStatus = NV_READ(Adapter, NvRegMIIStatus);
/* Clear the link change interrupt */ NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_LINKCHANGE);
if (MiiStatus & NVREG_MIISTAT_LINKCHANGE) { Connected = NvNetUpdateLinkSpeed(Adapter); if (Adapter->Connected != Connected) { Adapter->Connected = Connected; Report = TRUE;
if (Connected) { /* Link up */ NvNetToggleClockPowerGating(Adapter, FALSE); NdisDprAcquireSpinLock(&Adapter->Receive.Lock); NvNetStartReceiver(Adapter); } else { /* Link down */ NvNetToggleClockPowerGating(Adapter, TRUE); NdisDprAcquireSpinLock(&Adapter->Receive.Lock); NvNetStopReceiver(Adapter); }
NdisDprReleaseSpinLock(&Adapter->Receive.Lock); } }
NdisDprReleaseSpinLock(&Adapter->Lock);
if (Report) { NdisMIndicateStatus(Adapter->AdapterHandle, Connected ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0); NdisMIndicateStatusComplete(Adapter->AdapterHandle); }}
staticVOIDHandleRecoverableError( _In_ PNVNET_ADAPTER Adapter){ /* TODO */ NDIS_DbgPrint(MAX_TRACE, ("() Recoverable error detected\n"));}
VOIDNTAPIMiniportHandleInterrupt( _In_ NDIS_HANDLE MiniportAdapterContext){ PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; ULONG InterruptStatus = Adapter->InterruptStatus; ULONG RxProcessed, TotalTxProcessed = 0, TotalRxProcessed = 0; LIST_ENTRY SendReadyList;
NDIS_DbgPrint(MIN_TRACE, ("() Events 0x%lx\n", InterruptStatus));
if (!(Adapter->Flags & NV_ACTIVE)) return;
InitializeListHead(&SendReadyList);
/* Process the rings and measure network activity */ while (TotalRxProcessed < NVNET_RECEIVE_PROCESSING_LIMIT) { NdisDprAcquireSpinLock(&Adapter->Send.Lock);
TotalTxProcessed += Adapter->ProcessTransmit(Adapter, &SendReadyList);
NdisDprReleaseSpinLock(&Adapter->Send.Lock);
while (!IsListEmpty(&SendReadyList)) { PLIST_ENTRY Entry = RemoveHeadList(&SendReadyList);
NdisMSendComplete(Adapter->AdapterHandle, CONTAINING_RECORD(Entry, NDIS_PACKET, MiniportReserved), NDIS_STATUS_SUCCESS); }
RxProcessed = ProcessReceiveDescriptors(Adapter, TotalRxProcessed); if (!RxProcessed) break;
TotalRxProcessed += RxProcessed; }
NDIS_DbgPrint(MIN_TRACE, ("Total TX: %d, RX: %d\n", TotalTxProcessed, TotalRxProcessed));
/* Moderate the interrupts */ if (Adapter->OptimizationMode == NV_OPTIMIZATION_MODE_DYNAMIC) { ChangeInterruptMode(Adapter, TotalTxProcessed + TotalRxProcessed); }
if (InterruptStatus & NVREG_IRQ_RX_NOBUF) { ++Adapter->Statistics.ReceiveIrqNoBuffers; }
if (InterruptStatus & NVREG_IRQ_LINK) { HandleLinkStateChange(Adapter); }
if (InterruptStatus & NVREG_IRQ_RECOVER_ERROR) { HandleRecoverableError(Adapter); }
/* Enable interrupts on the NIC */ NvNetApplyInterruptMask(Adapter);}
VOIDNTAPIMiniportISR( _Out_ PBOOLEAN InterruptRecognized, _Out_ PBOOLEAN QueueMiniportHandleInterrupt, _In_ NDIS_HANDLE MiniportAdapterContext){ PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; ULONG InterruptStatus;
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
InterruptStatus = NV_READ(Adapter, NvRegIrqStatus);
/* Clear any interrupt events */ NV_WRITE(Adapter, NvRegIrqStatus, InterruptStatus);
if (InterruptStatus & Adapter->InterruptMask) { /* Disable further interrupts */ NvNetDisableInterrupts(Adapter);
Adapter->InterruptStatus = InterruptStatus;
*InterruptRecognized = TRUE; *QueueMiniportHandleInterrupt = TRUE; } else { /* This interrupt is not ours */ *InterruptRecognized = FALSE; *QueueMiniportHandleInterrupt = FALSE; }}