mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
b79fbe2333
The driver supports all nVidia chipset models from 2001 until 2010, starting from nForce. All NICs are compatible with x86 and amd64 devices only. Tested by Daniel Reimer on OG Xbox and by me on MCP board. CORE-15872 CORE-16216
549 lines
15 KiB
C
549 lines
15 KiB
C
/*
|
|
* 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 ******************************************************************/
|
|
|
|
ULONG
|
|
ProcessTransmitDescriptorsLegacy(
|
|
_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;
|
|
}
|
|
|
|
ULONG
|
|
ProcessTransmitDescriptors32(
|
|
_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;
|
|
}
|
|
|
|
ULONG
|
|
ProcessTransmitDescriptors64(
|
|
_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;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
HandleLengthError(
|
|
_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 */
|
|
static
|
|
ULONG
|
|
ProcessReceiveDescriptors(
|
|
_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;
|
|
}
|
|
|
|
static
|
|
inline
|
|
VOID
|
|
ChangeInterruptMode(
|
|
_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
HandleLinkStateChange(
|
|
_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);
|
|
}
|
|
}
|
|
|
|
static
|
|
VOID
|
|
HandleRecoverableError(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
/* TODO */
|
|
NDIS_DbgPrint(MAX_TRACE, ("() Recoverable error detected\n"));
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiniportHandleInterrupt(
|
|
_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);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiniportISR(
|
|
_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;
|
|
}
|
|
}
|