reactos/drivers/network/dd/nvnet/nic.c

928 lines
30 KiB
C
Raw Normal View History

/*
* 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;
}