mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +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
927 lines
30 KiB
C
927 lines
30 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: NIC support code
|
|
* COPYRIGHT: Copyright 2021-2022 Dmitry Borisov <di.sean@protonmail.com>
|
|
*/
|
|
|
|
/*
|
|
* HW access code was taken from the Linux forcedeth driver
|
|
* Copyright (C) 2003,4,5 Manfred Spraul
|
|
* Copyright (C) 2004 Andrew de Quincey
|
|
* Copyright (C) 2004 Carl-Daniel Hailfinger
|
|
* Copyright (c) 2004,2005,2006,2007,2008,2009 NVIDIA Corporation
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "nvnet.h"
|
|
|
|
#define NDEBUG
|
|
#include "debug.h"
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
NvNetClearStatisticsCounters(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
NVNET_REGISTER Counter, CounterEnd;
|
|
|
|
PAGED_CODE();
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
if (Adapter->Features & DEV_HAS_STATISTICS_V2)
|
|
CounterEnd = NvRegRxDropFrame;
|
|
else
|
|
CounterEnd = NvRegRxBroadcast;
|
|
|
|
for (Counter = NvRegTxCnt; Counter <= CounterEnd; Counter += sizeof(ULONG))
|
|
{
|
|
NV_READ(Adapter, Counter);
|
|
}
|
|
|
|
if (Adapter->Features & DEV_HAS_STATISTICS_V3)
|
|
{
|
|
NV_READ(Adapter, NvRegTxUnicast);
|
|
NV_READ(Adapter, NvRegTxMulticast);
|
|
NV_READ(Adapter, NvRegTxBroadcast);
|
|
}
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
NvNetResetMac(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
ULONG Temp[3];
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
if (!(Adapter->Features & DEV_HAS_POWER_CNTRL))
|
|
return;
|
|
|
|
NV_WRITE(Adapter, NvRegTxRxControl,
|
|
Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET);
|
|
|
|
/* Save registers since they will be cleared on reset */
|
|
Temp[0] = NV_READ(Adapter, NvRegMacAddrA);
|
|
Temp[1] = NV_READ(Adapter, NvRegMacAddrB);
|
|
Temp[2] = NV_READ(Adapter, NvRegTransmitPoll);
|
|
|
|
NV_WRITE(Adapter, NvRegMacReset, NVREG_MAC_RESET_ASSERT);
|
|
NdisStallExecution(NV_MAC_RESET_DELAY);
|
|
NV_WRITE(Adapter, NvRegMacReset, 0);
|
|
NdisStallExecution(NV_MAC_RESET_DELAY);
|
|
|
|
/* Restore saved registers */
|
|
NV_WRITE(Adapter, NvRegMacAddrA, Temp[0]);
|
|
NV_WRITE(Adapter, NvRegMacAddrB, Temp[1]);
|
|
NV_WRITE(Adapter, NvRegTransmitPoll, Temp[2]);
|
|
|
|
NV_WRITE(Adapter, NvRegTxRxControl,
|
|
Adapter->TxRxControl | NVREG_TXRXCTL_BIT2);
|
|
}
|
|
|
|
VOID
|
|
NvNetResetReceiverAndTransmitter(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
NV_WRITE(Adapter, NvRegTxRxControl,
|
|
Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET);
|
|
|
|
NdisStallExecution(NV_TXRX_RESET_DELAY);
|
|
|
|
NV_WRITE(Adapter, NvRegTxRxControl,
|
|
Adapter->TxRxControl | NVREG_TXRXCTL_BIT2);
|
|
}
|
|
|
|
VOID
|
|
NvNetStartReceiver(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
ULONG RxControl;
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
RxControl = NV_READ(Adapter, NvRegReceiverControl);
|
|
if ((NV_READ(Adapter, NvRegReceiverControl) & NVREG_RCVCTL_START) &&
|
|
!(Adapter->Flags & NV_MAC_IN_USE))
|
|
{
|
|
/* Already running? Stop it */
|
|
RxControl &= ~NVREG_RCVCTL_START;
|
|
NV_WRITE(Adapter, NvRegReceiverControl, RxControl);
|
|
}
|
|
NV_WRITE(Adapter, NvRegLinkSpeed, Adapter->LinkSpeed | NVREG_LINKSPEED_FORCE);
|
|
|
|
RxControl |= NVREG_RCVCTL_START;
|
|
if (Adapter->Flags & NV_MAC_IN_USE)
|
|
{
|
|
RxControl &= ~NVREG_RCVCTL_RX_PATH_EN;
|
|
}
|
|
NV_WRITE(Adapter, NvRegReceiverControl, RxControl);
|
|
}
|
|
|
|
VOID
|
|
NvNetStartTransmitter(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
ULONG TxControl;
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
TxControl = NV_READ(Adapter, NvRegTransmitterControl);
|
|
TxControl |= NVREG_XMITCTL_START;
|
|
if (Adapter->Flags & NV_MAC_IN_USE)
|
|
{
|
|
TxControl &= ~NVREG_XMITCTL_TX_PATH_EN;
|
|
}
|
|
NV_WRITE(Adapter, NvRegTransmitterControl, TxControl);
|
|
}
|
|
|
|
VOID
|
|
NvNetStopReceiver(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
ULONG RxControl, i;
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
RxControl = NV_READ(Adapter, NvRegReceiverControl);
|
|
if (!(Adapter->Flags & NV_MAC_IN_USE))
|
|
RxControl &= ~NVREG_RCVCTL_START;
|
|
else
|
|
RxControl |= NVREG_RCVCTL_RX_PATH_EN;
|
|
NV_WRITE(Adapter, NvRegReceiverControl, RxControl);
|
|
|
|
for (i = 0; i < NV_RXSTOP_DELAY1MAX; ++i)
|
|
{
|
|
if (!(NV_READ(Adapter, NvRegReceiverStatus) & NVREG_RCVSTAT_BUSY))
|
|
break;
|
|
|
|
NdisStallExecution(NV_RXSTOP_DELAY1);
|
|
}
|
|
|
|
NdisStallExecution(NV_RXSTOP_DELAY2);
|
|
|
|
if (!(Adapter->Flags & NV_MAC_IN_USE))
|
|
{
|
|
NV_WRITE(Adapter, NvRegLinkSpeed, 0);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NvNetStopTransmitter(
|
|
_In_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
ULONG TxControl, i;
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
TxControl = NV_READ(Adapter, NvRegTransmitterControl);
|
|
if (!(Adapter->Flags & NV_MAC_IN_USE))
|
|
TxControl &= ~NVREG_XMITCTL_START;
|
|
else
|
|
TxControl |= NVREG_XMITCTL_TX_PATH_EN;
|
|
NV_WRITE(Adapter, NvRegTransmitterControl, TxControl);
|
|
|
|
for (i = 0; i < NV_TXSTOP_DELAY1MAX; ++i)
|
|
{
|
|
if (!(NV_READ(Adapter, NvRegTransmitterStatus) & NVREG_XMITSTAT_BUSY))
|
|
break;
|
|
|
|
NdisStallExecution(NV_TXSTOP_DELAY1);
|
|
}
|
|
|
|
NdisStallExecution(NV_TXSTOP_DELAY2);
|
|
|
|
if (!(Adapter->Flags & NV_MAC_IN_USE))
|
|
{
|
|
NV_WRITE(Adapter, NvRegTransmitPoll,
|
|
NV_READ(Adapter, NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV);
|
|
}
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
NvNetIdleTransmitter(
|
|
_In_ PNVNET_ADAPTER Adapter,
|
|
_In_ BOOLEAN ClearPhyControl)
|
|
{
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (ClearPhyControl)
|
|
{
|
|
NV_WRITE(Adapter, NvRegAdapterControl,
|
|
NV_READ(Adapter, NvRegAdapterControl) & ~NVREG_ADAPTCTL_RUNNING);
|
|
}
|
|
else
|
|
{
|
|
NV_WRITE(Adapter, NvRegAdapterControl,
|
|
(Adapter->PhyAddress << NVREG_ADAPTCTL_PHYSHIFT) |
|
|
NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING);
|
|
}
|
|
|
|
NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl | NVREG_TXRXCTL_BIT2);
|
|
for (i = 0; i < NV_TXIDLE_ATTEMPTS; ++i)
|
|
{
|
|
if (NV_READ(Adapter, NvRegTxRxControl) & NVREG_TXRXCTL_IDLE)
|
|
break;
|
|
|
|
NdisStallExecution(NV_TXIDLE_DELAY);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NvNetUpdatePauseFrame(
|
|
_Inout_ PNVNET_ADAPTER Adapter,
|
|
_In_ ULONG PauseFlags)
|
|
{
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
Adapter->PauseFlags &= ~(NV_PAUSEFRAME_TX_ENABLE | NV_PAUSEFRAME_RX_ENABLE);
|
|
|
|
if (Adapter->PauseFlags & NV_PAUSEFRAME_RX_CAPABLE)
|
|
{
|
|
ULONG PacketFilter = NV_READ(Adapter, NvRegPacketFilterFlags) & ~NVREG_PFF_PAUSE_RX;
|
|
|
|
if (PauseFlags & NV_PAUSEFRAME_RX_ENABLE)
|
|
{
|
|
PacketFilter |= NVREG_PFF_PAUSE_RX;
|
|
Adapter->PauseFlags |= NV_PAUSEFRAME_RX_ENABLE;
|
|
}
|
|
NV_WRITE(Adapter, NvRegPacketFilterFlags, PacketFilter);
|
|
}
|
|
|
|
if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_CAPABLE)
|
|
{
|
|
ULONG Mics = NV_READ(Adapter, NvRegMisc1) & ~NVREG_MISC1_PAUSE_TX;
|
|
|
|
if (PauseFlags & NV_PAUSEFRAME_TX_ENABLE)
|
|
{
|
|
ULONG PauseEnable = NVREG_TX_PAUSEFRAME_ENABLE_V1;
|
|
|
|
if (Adapter->Features & DEV_HAS_PAUSEFRAME_TX_V2)
|
|
PauseEnable = NVREG_TX_PAUSEFRAME_ENABLE_V2;
|
|
if (Adapter->Features & DEV_HAS_PAUSEFRAME_TX_V3)
|
|
{
|
|
PauseEnable = NVREG_TX_PAUSEFRAME_ENABLE_V3;
|
|
/* Limit the number of TX pause frames to a default of 8 */
|
|
NV_WRITE(Adapter,
|
|
NvRegTxPauseFrameLimit,
|
|
NV_READ(Adapter, NvRegTxPauseFrameLimit) |
|
|
NVREG_TX_PAUSEFRAMELIMIT_ENABLE);
|
|
}
|
|
NV_WRITE(Adapter, NvRegTxPauseFrame, PauseEnable);
|
|
NV_WRITE(Adapter, NvRegMisc1, Mics | NVREG_MISC1_PAUSE_TX);
|
|
Adapter->PauseFlags |= NV_PAUSEFRAME_TX_ENABLE;
|
|
}
|
|
else
|
|
{
|
|
NV_WRITE(Adapter, NvRegTxPauseFrame, NVREG_TX_PAUSEFRAME_DISABLE);
|
|
NV_WRITE(Adapter, NvRegMisc1, Mics);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NvNetToggleClockPowerGating(
|
|
_In_ PNVNET_ADAPTER Adapter,
|
|
_In_ BOOLEAN Gate)
|
|
{
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
if (!(Adapter->Flags & NV_MAC_IN_USE) && (Adapter->Features & DEV_HAS_POWER_CNTRL))
|
|
{
|
|
ULONG PowerState = NV_READ(Adapter, NvRegPowerState2);
|
|
|
|
if (Gate)
|
|
PowerState |= NVREG_POWERSTATE2_GATE_CLOCKS;
|
|
else
|
|
PowerState &= ~NVREG_POWERSTATE2_GATE_CLOCKS;
|
|
NV_WRITE(Adapter, NvRegPowerState2, PowerState);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
NvNetMediaDetectionDpc(
|
|
_In_ PVOID SystemSpecific1,
|
|
_In_ PVOID FunctionContext,
|
|
_In_ PVOID SystemSpecific2,
|
|
_In_ PVOID SystemSpecific3)
|
|
{
|
|
PNVNET_ADAPTER Adapter = FunctionContext;
|
|
BOOLEAN Connected, Report = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER(SystemSpecific1);
|
|
UNREFERENCED_PARAMETER(SystemSpecific2);
|
|
UNREFERENCED_PARAMETER(SystemSpecific3);
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
NdisDprAcquireSpinLock(&Adapter->Lock);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
NvNetInitPhaseSynchronized(
|
|
_In_ PVOID SynchronizeContext)
|
|
{
|
|
PNVNET_ADAPTER Adapter = SynchronizeContext;
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
/* Enable interrupts on the NIC */
|
|
NvNetApplyInterruptMask(Adapter);
|
|
|
|
/*
|
|
* One manual link speed update: Interrupts are enabled,
|
|
* future link speed changes cause interrupts.
|
|
*/
|
|
NV_READ(Adapter, NvRegMIIStatus);
|
|
NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_ALL);
|
|
|
|
/* Set link speed to invalid value, thus force NvNetUpdateLinkSpeed() to init HW */
|
|
Adapter->LinkSpeed = 0xFFFFFFFF;
|
|
|
|
Adapter->Connected = NvNetUpdateLinkSpeed(Adapter);
|
|
|
|
NvNetStartReceiver(Adapter);
|
|
NvNetStartTransmitter(Adapter);
|
|
|
|
Adapter->Flags |= NV_ACTIVE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NDIS_STATUS
|
|
NvNetInitNIC(
|
|
_In_ PNVNET_ADAPTER Adapter,
|
|
_In_ BOOLEAN InitPhy)
|
|
{
|
|
ULONG MiiControl, i;
|
|
NDIS_STATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
/* Disable WOL */
|
|
NV_WRITE(Adapter, NvRegWakeUpFlags, 0);
|
|
|
|
if (InitPhy)
|
|
{
|
|
Status = NvNetPhyInit(Adapter);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_CAPABLE)
|
|
{
|
|
NV_WRITE(Adapter, NvRegTxPauseFrame, NVREG_TX_PAUSEFRAME_DISABLE);
|
|
}
|
|
|
|
/* Power up PHY */
|
|
MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl);
|
|
MiiControl &= ~MII_CR_POWER_DOWN;
|
|
MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl);
|
|
|
|
NvNetToggleClockPowerGating(Adapter, FALSE);
|
|
|
|
NvNetResetMac(Adapter);
|
|
|
|
/* Clear multicast masks and addresses */
|
|
NV_WRITE(Adapter, NvRegMulticastAddrA, 0);
|
|
NV_WRITE(Adapter, NvRegMulticastAddrB, 0);
|
|
NV_WRITE(Adapter, NvRegMulticastMaskA, NVREG_MCASTMASKA_NONE);
|
|
NV_WRITE(Adapter, NvRegMulticastMaskB, NVREG_MCASTMASKB_NONE);
|
|
|
|
NV_WRITE(Adapter, NvRegTransmitterControl, 0);
|
|
NV_WRITE(Adapter, NvRegReceiverControl, 0);
|
|
|
|
NV_WRITE(Adapter, NvRegAdapterControl, 0);
|
|
|
|
NV_WRITE(Adapter, NvRegLinkSpeed, 0);
|
|
NV_WRITE(Adapter, NvRegTransmitPoll,
|
|
NV_READ(Adapter, NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV);
|
|
NvNetResetReceiverAndTransmitter(Adapter);
|
|
NV_WRITE(Adapter, NvRegUnknownSetupReg6, 0);
|
|
|
|
/* Receive descriptor ring buffer */
|
|
NV_WRITE(Adapter, NvRegRxRingPhysAddr,
|
|
NdisGetPhysicalAddressLow(Adapter->RbdPhys));
|
|
if (Adapter->Features & DEV_HAS_HIGH_DMA)
|
|
{
|
|
NV_WRITE(Adapter, NvRegRxRingPhysAddrHigh,
|
|
NdisGetPhysicalAddressHigh(Adapter->RbdPhys));
|
|
}
|
|
|
|
/* Transmit descriptor ring buffer */
|
|
NV_WRITE(Adapter, NvRegTxRingPhysAddr,
|
|
NdisGetPhysicalAddressLow(Adapter->TbdPhys));
|
|
if (Adapter->Features & DEV_HAS_HIGH_DMA)
|
|
{
|
|
NV_WRITE(Adapter, NvRegTxRingPhysAddrHigh,
|
|
NdisGetPhysicalAddressHigh(Adapter->TbdPhys));
|
|
}
|
|
|
|
/* Ring sizes */
|
|
NV_WRITE(Adapter, NvRegRingSizes,
|
|
(NVNET_RECEIVE_DESCRIPTORS - 1) << NVREG_RINGSZ_RXSHIFT |
|
|
(NVNET_TRANSMIT_DESCRIPTORS - 1) << NVREG_RINGSZ_TXSHIFT);
|
|
|
|
/* Set default link speed settings */
|
|
NV_WRITE(Adapter, NvRegLinkSpeed, NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10);
|
|
|
|
if (Adapter->Features & (DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC))
|
|
NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC2_3_DEFAULT);
|
|
else
|
|
NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC1_DEFAULT);
|
|
|
|
NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl);
|
|
NV_WRITE(Adapter, NvRegVlanControl, Adapter->VlanControl);
|
|
NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl | NVREG_TXRXCTL_BIT1);
|
|
|
|
for (i = 0; i < NV_SETUP5_DELAYMAX; ++i)
|
|
{
|
|
if (NV_READ(Adapter, NvRegUnknownSetupReg5) & NVREG_UNKSETUP5_BIT31)
|
|
break;
|
|
|
|
NdisStallExecution(NV_SETUP5_DELAY);
|
|
}
|
|
|
|
NV_WRITE(Adapter, NvRegMIIMask, 0);
|
|
NV_WRITE(Adapter, NvRegIrqStatus, NVREG_IRQSTAT_MASK);
|
|
NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_ALL);
|
|
|
|
NV_WRITE(Adapter, NvRegMisc1, NVREG_MISC1_FORCE | NVREG_MISC1_HD);
|
|
NV_WRITE(Adapter, NvRegTransmitterStatus, NV_READ(Adapter, NvRegTransmitterStatus));
|
|
NV_WRITE(Adapter, NvRegPacketFilterFlags, NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR);
|
|
NV_WRITE(Adapter, NvRegOffloadConfig, (NVNET_MAXIMUM_FRAME_SIZE - sizeof(ETH_HEADER))
|
|
+ NV_RX_HEADERS);
|
|
|
|
NV_WRITE(Adapter, NvRegReceiverStatus, NV_READ(Adapter, NvRegReceiverStatus));
|
|
|
|
NvNetBackoffSetSlotTime(Adapter);
|
|
|
|
NV_WRITE(Adapter, NvRegTxDeferral, NVREG_TX_DEFERRAL_DEFAULT);
|
|
NV_WRITE(Adapter, NvRegRxDeferral, NVREG_RX_DEFERRAL_DEFAULT);
|
|
|
|
if (Adapter->OptimizationMode == NV_OPTIMIZATION_MODE_THROUGHPUT)
|
|
NV_WRITE(Adapter, NvRegPollingInterval, NVREG_POLL_DEFAULT_THROUGHPUT);
|
|
else
|
|
NV_WRITE(Adapter, NvRegPollingInterval, NVREG_POLL_DEFAULT_CPU);
|
|
NV_WRITE(Adapter, NvRegUnknownSetupReg6, NVREG_UNKSETUP6_VAL);
|
|
|
|
NV_WRITE(Adapter, NvRegAdapterControl,
|
|
(Adapter->PhyAddress << NVREG_ADAPTCTL_PHYSHIFT) |
|
|
NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING);
|
|
NV_WRITE(Adapter, NvRegMIISpeed, NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY);
|
|
NV_WRITE(Adapter, NvRegMIIMask, NVREG_MII_LINKCHANGE);
|
|
|
|
NdisStallExecution(10);
|
|
NV_WRITE(Adapter, NvRegPowerState,
|
|
NV_READ(Adapter, NvRegPowerState) & ~NVREG_POWERSTATE_VALID);
|
|
|
|
if (Adapter->Features & DEV_HAS_STATISTICS_COUNTERS)
|
|
{
|
|
NvNetClearStatisticsCounters(Adapter);
|
|
}
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NDIS_STATUS
|
|
NvNetGetPermanentMacAddress(
|
|
_Inout_ PNVNET_ADAPTER Adapter,
|
|
_Out_writes_bytes_all_(ETH_LENGTH_OF_ADDRESS) PUCHAR MacAddress)
|
|
{
|
|
ULONG Temp[2], TxPoll;
|
|
|
|
PAGED_CODE();
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
Temp[0] = NV_READ(Adapter, NvRegMacAddrA);
|
|
Temp[1] = NV_READ(Adapter, NvRegMacAddrB);
|
|
|
|
TxPoll = NV_READ(Adapter, NvRegTransmitPoll);
|
|
|
|
if (Adapter->Features & DEV_HAS_CORRECT_MACADDR)
|
|
{
|
|
/* MAC address is already in the correct order */
|
|
MacAddress[0] = (Temp[0] >> 0) & 0xFF;
|
|
MacAddress[1] = (Temp[0] >> 8) & 0xFF;
|
|
MacAddress[2] = (Temp[0] >> 16) & 0xFF;
|
|
MacAddress[3] = (Temp[0] >> 24) & 0xFF;
|
|
MacAddress[4] = (Temp[1] >> 0) & 0xFF;
|
|
MacAddress[5] = (Temp[1] >> 8) & 0xFF;
|
|
}
|
|
/* Handle the special flag for the correct MAC address order */
|
|
else if (TxPoll & NVREG_TRANSMITPOLL_MAC_ADDR_REV)
|
|
{
|
|
/* MAC address is already in the correct order */
|
|
MacAddress[0] = (Temp[0] >> 0) & 0xFF;
|
|
MacAddress[1] = (Temp[0] >> 8) & 0xFF;
|
|
MacAddress[2] = (Temp[0] >> 16) & 0xFF;
|
|
MacAddress[3] = (Temp[0] >> 24) & 0xFF;
|
|
MacAddress[4] = (Temp[1] >> 0) & 0xFF;
|
|
MacAddress[5] = (Temp[1] >> 8) & 0xFF;
|
|
|
|
/*
|
|
* Set original MAC address back to the reversed version.
|
|
* This flag will be cleared during low power transition.
|
|
* Therefore, we should always put back the reversed address.
|
|
*/
|
|
Temp[0] = (MacAddress[5] << 0) | (MacAddress[4] << 8) |
|
|
(MacAddress[3] << 16) | (MacAddress[2] << 24);
|
|
Temp[1] = (MacAddress[1] << 0) | (MacAddress[0] << 8);
|
|
}
|
|
else
|
|
{
|
|
/* Need to reverse MAC address to the correct order */
|
|
MacAddress[0] = (Temp[1] >> 8) & 0xFF;
|
|
MacAddress[1] = (Temp[1] >> 0) & 0xFF;
|
|
MacAddress[2] = (Temp[0] >> 24) & 0xFF;
|
|
MacAddress[3] = (Temp[0] >> 16) & 0xFF;
|
|
MacAddress[4] = (Temp[0] >> 8) & 0xFF;
|
|
MacAddress[5] = (Temp[0] >> 0) & 0xFF;
|
|
|
|
/*
|
|
* Use a flag to signal the driver whether the MAC address was already corrected,
|
|
* so that it is not reversed again on a subsequent initialize.
|
|
*/
|
|
NV_WRITE(Adapter, NvRegTransmitPoll, TxPoll | NVREG_TRANSMITPOLL_MAC_ADDR_REV);
|
|
}
|
|
|
|
Adapter->OriginalMacAddress[0] = Temp[0];
|
|
Adapter->OriginalMacAddress[1] = Temp[1];
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
MacAddress[0],
|
|
MacAddress[1],
|
|
MacAddress[2],
|
|
MacAddress[3],
|
|
MacAddress[4],
|
|
MacAddress[5]));
|
|
|
|
if (ETH_IS_MULTICAST(MacAddress) || ETH_IS_EMPTY(MacAddress))
|
|
return NDIS_STATUS_INVALID_ADDRESS;
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
VOID
|
|
NvNetSetupMacAddress(
|
|
_In_ PNVNET_ADAPTER Adapter,
|
|
_In_reads_bytes_(ETH_LENGTH_OF_ADDRESS) PUCHAR MacAddress)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
NV_WRITE(Adapter, NvRegMacAddrA,
|
|
MacAddress[3] << 24 | MacAddress[2] << 16 | MacAddress[1] << 8 | MacAddress[0]);
|
|
NV_WRITE(Adapter, NvRegMacAddrB, MacAddress[5] << 8 | MacAddress[4]);
|
|
}
|
|
|
|
static
|
|
VOID
|
|
CODE_SEG("PAGE")
|
|
NvNetValidateConfiguration(
|
|
_Inout_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (!(Adapter->Features & DEV_HAS_LARGEDESC))
|
|
{
|
|
Adapter->MaximumFrameSize = NVNET_MAXIMUM_FRAME_SIZE;
|
|
}
|
|
if (!(Adapter->Features & DEV_HAS_CHECKSUM))
|
|
{
|
|
Adapter->Flags &= ~(NV_SEND_CHECKSUM | NV_SEND_LARGE_SEND);
|
|
}
|
|
if (!(Adapter->Features & DEV_HAS_VLAN))
|
|
{
|
|
Adapter->Flags &= ~(NV_PACKET_PRIORITY | NV_VLAN_TAGGING);
|
|
}
|
|
if ((Adapter->Features & DEV_NEED_TIMERIRQ) &&
|
|
(Adapter->OptimizationMode == NV_OPTIMIZATION_MODE_DYNAMIC))
|
|
{
|
|
Adapter->OptimizationMode = NV_OPTIMIZATION_MODE_THROUGHPUT;
|
|
}
|
|
if (!(Adapter->Features & DEV_HAS_TX_PAUSEFRAME))
|
|
{
|
|
if (Adapter->FlowControlMode == NV_FLOW_CONTROL_TX)
|
|
{
|
|
Adapter->FlowControlMode = NV_FLOW_CONTROL_AUTO;
|
|
}
|
|
else if (Adapter->FlowControlMode == NV_FLOW_CONTROL_RX_TX)
|
|
{
|
|
Adapter->FlowControlMode = NV_FLOW_CONTROL_RX;
|
|
}
|
|
}
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NDIS_STATUS
|
|
NvNetRecognizeHardware(
|
|
_Inout_ PNVNET_ADAPTER Adapter)
|
|
{
|
|
ULONG Bytes;
|
|
PCI_COMMON_CONFIG PciConfig;
|
|
|
|
PAGED_CODE();
|
|
|
|
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
|
|
|
|
Bytes = NdisReadPciSlotInformation(Adapter->AdapterHandle,
|
|
0,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID),
|
|
&PciConfig,
|
|
PCI_COMMON_HDR_LENGTH);
|
|
if (Bytes != PCI_COMMON_HDR_LENGTH)
|
|
return NDIS_STATUS_ADAPTER_NOT_FOUND;
|
|
|
|
if (PciConfig.VendorID != 0x10DE)
|
|
return NDIS_STATUS_ADAPTER_NOT_FOUND;
|
|
|
|
Adapter->DeviceId = PciConfig.DeviceID;
|
|
Adapter->RevisionId = PciConfig.RevisionID;
|
|
|
|
switch (PciConfig.DeviceID)
|
|
{
|
|
case 0x01C3: /* nForce */
|
|
case 0x0066: /* nForce2 */
|
|
case 0x00D6: /* nForce2 */
|
|
Adapter->Features = DEV_NEED_TIMERIRQ | DEV_NEED_LINKTIMER;
|
|
break;
|
|
|
|
case 0x0086: /* nForce3 */
|
|
case 0x008C: /* nForce3 */
|
|
case 0x00E6: /* nForce3 */
|
|
case 0x00DF: /* nForce3 */
|
|
Adapter->Features = DEV_NEED_TIMERIRQ | DEV_NEED_LINKTIMER |
|
|
DEV_HAS_LARGEDESC | DEV_HAS_CHECKSUM;
|
|
break;
|
|
|
|
case 0x0056: /* CK804 */
|
|
case 0x0057: /* CK804 */
|
|
case 0x0037: /* MCP04 */
|
|
case 0x0038: /* MCP04 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_LARGEDESC | DEV_HAS_CHECKSUM |
|
|
DEV_HAS_HIGH_DMA | DEV_HAS_STATISTICS_V1 | DEV_NEED_TX_LIMIT;
|
|
break;
|
|
|
|
case 0x0268: /* MCP51 */
|
|
case 0x0269: /* MCP51 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_HIGH_DMA | DEV_HAS_POWER_CNTRL |
|
|
DEV_HAS_STATISTICS_V1 | DEV_NEED_LOW_POWER_FIX;
|
|
break;
|
|
|
|
case 0x0372: /* MCP55 */
|
|
case 0x0373: /* MCP55 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_LARGEDESC | DEV_HAS_CHECKSUM |
|
|
DEV_HAS_HIGH_DMA | DEV_HAS_VLAN | DEV_HAS_MSI | DEV_HAS_MSI_X |
|
|
DEV_HAS_POWER_CNTRL | DEV_HAS_PAUSEFRAME_TX_V1 |
|
|
DEV_HAS_STATISTICS_V1 | DEV_HAS_STATISTICS_V2 |
|
|
DEV_HAS_TEST_EXTENDED | DEV_HAS_MGMT_UNIT |
|
|
DEV_NEED_TX_LIMIT | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x03E5: /* MCP61 */
|
|
case 0x03E6: /* MCP61 */
|
|
case 0x03EE: /* MCP61 */
|
|
case 0x03EF: /* MCP61 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_HIGH_DMA | DEV_HAS_POWER_CNTRL |
|
|
DEV_HAS_MSI | DEV_HAS_PAUSEFRAME_TX_V1 | DEV_HAS_STATISTICS_V1 |
|
|
DEV_HAS_STATISTICS_V2 | DEV_HAS_TEST_EXTENDED | DEV_HAS_MGMT_UNIT |
|
|
DEV_HAS_CORRECT_MACADDR | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x0450: /* MCP65 */
|
|
case 0x0451: /* MCP65 */
|
|
case 0x0452: /* MCP65 */
|
|
case 0x0453: /* MCP65 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_LARGEDESC | DEV_HAS_HIGH_DMA |
|
|
DEV_HAS_POWER_CNTRL | DEV_HAS_MSI | DEV_HAS_PAUSEFRAME_TX_V1 |
|
|
DEV_HAS_STATISTICS_V1 | DEV_HAS_STATISTICS_V2 |
|
|
DEV_HAS_TEST_EXTENDED | DEV_HAS_MGMT_UNIT |
|
|
DEV_HAS_CORRECT_MACADDR | DEV_NEED_TX_LIMIT |
|
|
DEV_HAS_GEAR_MODE | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x054C: /* MCP67 */
|
|
case 0x054D: /* MCP67 */
|
|
case 0x054E: /* MCP67 */
|
|
case 0x054F: /* MCP67 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_HIGH_DMA | DEV_HAS_POWER_CNTRL |
|
|
DEV_HAS_MSI | DEV_HAS_PAUSEFRAME_TX_V1 | DEV_HAS_STATISTICS_V1 |
|
|
DEV_HAS_STATISTICS_V2 | DEV_HAS_TEST_EXTENDED | DEV_HAS_MGMT_UNIT |
|
|
DEV_HAS_CORRECT_MACADDR | DEV_HAS_GEAR_MODE | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x07DC: /* MCP73 */
|
|
case 0x07DD: /* MCP73 */
|
|
case 0x07DE: /* MCP73 */
|
|
case 0x07DF: /* MCP73 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_HIGH_DMA | DEV_HAS_POWER_CNTRL |
|
|
DEV_HAS_MSI | DEV_HAS_PAUSEFRAME_TX_V1 | DEV_HAS_STATISTICS_V1 |
|
|
DEV_HAS_STATISTICS_V2 | DEV_HAS_TEST_EXTENDED | DEV_HAS_MGMT_UNIT |
|
|
DEV_HAS_CORRECT_MACADDR | DEV_HAS_COLLISION_FIX |
|
|
DEV_HAS_GEAR_MODE | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x0760: /* MCP77 */
|
|
case 0x0761: /* MCP77 */
|
|
case 0x0762: /* MCP77 */
|
|
case 0x0763: /* MCP77 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_CHECKSUM | DEV_HAS_HIGH_DMA |
|
|
DEV_HAS_MSI | DEV_HAS_POWER_CNTRL | DEV_HAS_PAUSEFRAME_TX_V2 |
|
|
DEV_HAS_STATISTICS_V1 | DEV_HAS_STATISTICS_V2 |
|
|
DEV_HAS_STATISTICS_V3 | DEV_HAS_TEST_EXTENDED | DEV_HAS_MGMT_UNIT |
|
|
DEV_HAS_CORRECT_MACADDR | DEV_HAS_COLLISION_FIX |
|
|
DEV_NEED_TX_LIMIT2 | DEV_HAS_GEAR_MODE |
|
|
DEV_NEED_PHY_INIT_FIX | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x0AB0: /* MCP79 */
|
|
case 0x0AB1: /* MCP79 */
|
|
case 0x0AB2: /* MCP79 */
|
|
case 0x0AB3: /* MCP79 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_LARGEDESC | DEV_HAS_CHECKSUM |
|
|
DEV_HAS_HIGH_DMA | DEV_HAS_MSI | DEV_HAS_POWER_CNTRL |
|
|
DEV_HAS_PAUSEFRAME_TX_V3 | DEV_HAS_STATISTICS_V1 |
|
|
DEV_HAS_STATISTICS_V2 | DEV_HAS_STATISTICS_V3 |
|
|
DEV_HAS_TEST_EXTENDED | DEV_HAS_CORRECT_MACADDR |
|
|
DEV_HAS_COLLISION_FIX | DEV_NEED_TX_LIMIT2 |
|
|
DEV_HAS_GEAR_MODE | DEV_NEED_PHY_INIT_FIX | DEV_NEED_MSI_FIX;
|
|
break;
|
|
|
|
case 0x0D7D: /* MCP89 */
|
|
Adapter->Features = DEV_NEED_LINKTIMER | DEV_HAS_LARGEDESC | DEV_HAS_CHECKSUM |
|
|
DEV_HAS_HIGH_DMA | DEV_HAS_MSI | DEV_HAS_POWER_CNTRL |
|
|
DEV_HAS_PAUSEFRAME_TX_V3 | DEV_HAS_STATISTICS_V1 |
|
|
DEV_HAS_STATISTICS_V2 | DEV_HAS_STATISTICS_V3 |
|
|
DEV_HAS_TEST_EXTENDED | DEV_HAS_CORRECT_MACADDR |
|
|
DEV_HAS_COLLISION_FIX | DEV_HAS_GEAR_MODE | DEV_NEED_PHY_INIT_FIX;
|
|
break;
|
|
|
|
default:
|
|
return NDIS_STATUS_NOT_RECOGNIZED;
|
|
}
|
|
|
|
/* Normalize all .INF parameters */
|
|
NvNetValidateConfiguration(Adapter);
|
|
|
|
/* FIXME: Disable some NIC features, we don't support these yet */
|
|
#if 1
|
|
Adapter->VlanControl = 0;
|
|
Adapter->Flags &= ~(NV_SEND_CHECKSUM | NV_SEND_LARGE_SEND |
|
|
NV_PACKET_PRIORITY | NV_VLAN_TAGGING);
|
|
#endif
|
|
|
|
/* For code paths debugging (32-bit descriptors work on all hardware variants) */
|
|
#if 0
|
|
Adapter->Features &= ~(DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC);
|
|
#endif
|
|
|
|
if (Adapter->Features & DEV_HAS_POWER_CNTRL)
|
|
Adapter->WakeFrameBitmap = ~(0xFFFFFFFF << NV_WAKEUPPATTERNS_V2);
|
|
else
|
|
Adapter->WakeFrameBitmap = ~(0xFFFFFFFF << NV_WAKEUPPATTERNS);
|
|
|
|
/* 64-bit descriptors */
|
|
if (Adapter->Features & DEV_HAS_HIGH_DMA)
|
|
{
|
|
/* Note: Some devices here also support Jumbo Frames */
|
|
Adapter->TxRxControl = NVREG_TXRXCTL_DESC_3;
|
|
}
|
|
/* 32-bit descriptors */
|
|
else
|
|
{
|
|
if (Adapter->Features & DEV_HAS_LARGEDESC)
|
|
{
|
|
/* Jumbo Frames */
|
|
Adapter->TxRxControl = NVREG_TXRXCTL_DESC_2;
|
|
}
|
|
else
|
|
{
|
|
/* Original packet format */
|
|
Adapter->TxRxControl = NVREG_TXRXCTL_DESC_1;
|
|
}
|
|
}
|
|
|
|
/* Flow control */
|
|
Adapter->PauseFlags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
|
|
if (Adapter->Features & DEV_HAS_TX_PAUSEFRAME)
|
|
{
|
|
Adapter->PauseFlags |= NV_PAUSEFRAME_TX_CAPABLE | NV_PAUSEFRAME_TX_REQ;
|
|
}
|
|
if (Adapter->FlowControlMode != NV_FLOW_CONTROL_AUTO)
|
|
{
|
|
Adapter->PauseFlags &= ~(NV_PAUSEFRAME_AUTONEG | NV_PAUSEFRAME_RX_REQ |
|
|
NV_PAUSEFRAME_TX_REQ);
|
|
switch (Adapter->FlowControlMode)
|
|
{
|
|
case NV_FLOW_CONTROL_RX:
|
|
Adapter->PauseFlags |= NV_PAUSEFRAME_RX_REQ;
|
|
break;
|
|
case NV_FLOW_CONTROL_TX:
|
|
Adapter->PauseFlags |= NV_PAUSEFRAME_TX_REQ;
|
|
break;
|
|
case NV_FLOW_CONTROL_RX_TX:
|
|
Adapter->PauseFlags |= NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_TX_REQ;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Work around errata in some NICs */
|
|
if (Adapter->Features & (DEV_NEED_TX_LIMIT | DEV_NEED_TX_LIMIT2))
|
|
{
|
|
Adapter->Flags |= NV_SEND_ERRATA_PRESENT;
|
|
|
|
if ((Adapter->Features & DEV_NEED_TX_LIMIT2) && Adapter->RevisionId >= 0xA2)
|
|
{
|
|
Adapter->Flags &= ~NV_SEND_ERRATA_PRESENT;
|
|
}
|
|
}
|
|
if (Adapter->Flags & NV_SEND_ERRATA_PRESENT)
|
|
{
|
|
NDIS_DbgPrint(MIN_TRACE, ("Transmit workaround active\n"));
|
|
}
|
|
|
|
/* Initialize the interrupt mask */
|
|
if (Adapter->OptimizationMode == NV_OPTIMIZATION_MODE_CPU)
|
|
{
|
|
Adapter->InterruptMask = NVREG_IRQMASK_CPU;
|
|
}
|
|
else
|
|
{
|
|
Adapter->InterruptMask = NVREG_IRQMASK_THROUGHPUT;
|
|
}
|
|
if (Adapter->Features & DEV_NEED_TIMERIRQ)
|
|
{
|
|
Adapter->InterruptMask |= NVREG_IRQ_TIMER;
|
|
}
|
|
|
|
if (Adapter->Features & DEV_NEED_LINKTIMER)
|
|
{
|
|
NdisMInitializeTimer(&Adapter->MediaDetectionTimer,
|
|
Adapter->AdapterHandle,
|
|
NvNetMediaDetectionDpc,
|
|
Adapter);
|
|
}
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|