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