diff --git a/drivers/network/dd/CMakeLists.txt b/drivers/network/dd/CMakeLists.txt index 39a20fce8e3..f8b967412a3 100644 --- a/drivers/network/dd/CMakeLists.txt +++ b/drivers/network/dd/CMakeLists.txt @@ -2,5 +2,8 @@ add_subdirectory(e1000) add_subdirectory(ne2000) add_subdirectory(netkvm) +if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64") + add_subdirectory(nvnet) +endif() add_subdirectory(pcnet) add_subdirectory(rtl8139) diff --git a/drivers/network/dd/nvnet/CMakeLists.txt b/drivers/network/dd/nvnet/CMakeLists.txt new file mode 100644 index 00000000000..81d38fe1c6e --- /dev/null +++ b/drivers/network/dd/nvnet/CMakeLists.txt @@ -0,0 +1,25 @@ + +add_definitions( + -DNDIS_MINIPORT_DRIVER + -DNDIS51_MINIPORT=1) + +list(APPEND SOURCE + backoff.c + debug.h + init.c + interrupt.c + nic.c + nic.h + nvnet.c + nvnet.h + phy.c + phyreg.h + requests.c + send.c) + +add_library(nvnet MODULE ${SOURCE} nvnet.rc) +add_pch(nvnet nvnet.h SOURCE) +set_module_type(nvnet kernelmodedriver) +add_importlibs(nvnet ndis ntoskrnl hal) +add_cd_file(TARGET nvnet DESTINATION reactos/system32/drivers FOR all) +add_driver_inf(nvnet netnv.inf) diff --git a/drivers/network/dd/nvnet/backoff.c b/drivers/network/dd/nvnet/backoff.c new file mode 100644 index 00000000000..3dc91ed12ec --- /dev/null +++ b/drivers/network/dd/nvnet/backoff.c @@ -0,0 +1,184 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Re-seeding random values for the backoff algorithms + * 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" + +/* GLOBALS ********************************************************************/ + +#define BACKOFF_SEEDSET_ROWS 8 +#define BACKOFF_SEEDSET_LFSRS 15 + +#define REVERSE_SEED(s) ((((s) & 0xF00) >> 8) | ((s) & 0x0F0) | (((s) & 0x00F) << 8)) + +static const ULONG NvpMainSeedSet[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = +{ + {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874}, + {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 385, 761, 790, 974}, + {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874}, + {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 386, 761, 790, 974}, + {266, 265, 276, 585, 397, 208, 345, 355, 365, 376, 385, 396, 771, 700, 984}, + {266, 265, 276, 586, 397, 208, 346, 355, 365, 376, 285, 396, 771, 700, 984}, + {366, 365, 376, 686, 497, 308, 447, 455, 466, 476, 485, 496, 871, 800, 84}, + {466, 465, 476, 786, 597, 408, 547, 555, 566, 576, 585, 597, 971, 900, 184} +}; + +static const ULONG NvpGearSeedSet[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = +{ + {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 397}, + {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295}, + {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}, + {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395} +}; + +/* FUNCTIONS ******************************************************************/ + +CODE_SEG("PAGE") +VOID +NvNetBackoffSetSlotTime( + _In_ PNVNET_ADAPTER Adapter) +{ + LARGE_INTEGER Sample = KeQueryPerformanceCounter(NULL); + + PAGED_CODE(); + + if ((Sample.LowPart & NVREG_SLOTTIME_MASK) == 0) + { + Sample.LowPart = 8; + } + + if (Adapter->Features & (DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC)) + { + if (Adapter->Features & DEV_HAS_GEAR_MODE) + { + NV_WRITE(Adapter, NvRegSlotTime, NVREG_SLOTTIME_10_100_FULL); + NvNetBackoffReseedEx(Adapter); + } + else + { + NV_WRITE(Adapter, NvRegSlotTime, (Sample.LowPart & NVREG_SLOTTIME_MASK) | + NVREG_SLOTTIME_LEGBF_ENABLED | NVREG_SLOTTIME_10_100_FULL); + } + } + else + { + NV_WRITE(Adapter, NvRegSlotTime, + (Sample.LowPart & NVREG_SLOTTIME_MASK) | NVREG_SLOTTIME_DEFAULT); + } +} + +VOID +NvNetBackoffReseed( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG SlotTime; + BOOLEAN RestartTransmitter = FALSE; + LARGE_INTEGER Sample = KeQueryPerformanceCounter(NULL); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if ((Sample.LowPart & NVREG_SLOTTIME_MASK) == 0) + { + Sample.LowPart = 8; + } + + SlotTime = NV_READ(Adapter, NvRegSlotTime) & ~NVREG_SLOTTIME_MASK; + SlotTime |= Sample.LowPart & NVREG_SLOTTIME_MASK; + + if (NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_START) + { + RestartTransmitter = TRUE; + NvNetStopTransmitter(Adapter); + } + NvNetStopReceiver(Adapter); + + NV_WRITE(Adapter, NvRegSlotTime, SlotTime); + + if (RestartTransmitter) + { + NvNetStartTransmitter(Adapter); + } + NvNetStartReceiver(Adapter); +} + +VOID +NvNetBackoffReseedEx( + _In_ PNVNET_ADAPTER Adapter) +{ + LARGE_INTEGER Sample; + ULONG Seed[3], ReversedSeed[2], CombinedSeed, SeedSet; + ULONG i, Temp; + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + Sample = KeQueryPerformanceCounter(NULL); + Seed[0] = Sample.LowPart & 0x0FFF; + if (Seed[0] == 0) + { + Seed[0] = 0x0ABC; + } + + Sample = KeQueryPerformanceCounter(NULL); + Seed[1] = Sample.LowPart & 0x0FFF; + if (Seed[1] == 0) + { + Seed[1] = 0x0ABC; + } + ReversedSeed[0] = REVERSE_SEED(Seed[1]); + + Sample = KeQueryPerformanceCounter(NULL); + Seed[2] = Sample.LowPart & 0x0FFF; + if (Seed[2] == 0) + { + Seed[2] = 0x0ABC; + } + ReversedSeed[1] = REVERSE_SEED(Seed[2]); + + CombinedSeed = ((Seed[0] ^ ReversedSeed[0]) << 12) | (Seed[1] ^ ReversedSeed[1]); + if ((CombinedSeed & NVREG_BKOFFCTRL_SEED_MASK) == 0) + { + CombinedSeed |= 8; + } + if ((CombinedSeed & (NVREG_BKOFFCTRL_SEED_MASK << NVREG_BKOFFCTRL_GEAR)) == 0) + { + CombinedSeed |= 8 << NVREG_BKOFFCTRL_GEAR; + } + + /* No need to disable transmitter here */ + Temp = NVREG_BKOFFCTRL_DEFAULT | (0 << NVREG_BKOFFCTRL_SELECT); + Temp |= CombinedSeed & NVREG_BKOFFCTRL_SEED_MASK; + Temp |= CombinedSeed >> NVREG_BKOFFCTRL_GEAR; + NV_WRITE(Adapter, NvRegBackOffControl, Temp); + + /* Setup seeds for all gear LFSRs */ + Sample = KeQueryPerformanceCounter(NULL); + SeedSet = Sample.LowPart % BACKOFF_SEEDSET_ROWS; + for (i = 1; i <= BACKOFF_SEEDSET_LFSRS; ++i) + { + Temp = NVREG_BKOFFCTRL_DEFAULT | (i << NVREG_BKOFFCTRL_SELECT); + Temp |= NvpMainSeedSet[SeedSet][i - 1] & NVREG_BKOFFCTRL_SEED_MASK; + Temp |= (NvpGearSeedSet[SeedSet][i - 1] & NVREG_BKOFFCTRL_SEED_MASK) + << NVREG_BKOFFCTRL_GEAR; + NV_WRITE(Adapter, NvRegBackOffControl, Temp); + } +} diff --git a/drivers/network/dd/nvnet/debug.h b/drivers/network/dd/nvnet/debug.h new file mode 100644 index 00000000000..5d57903f561 --- /dev/null +++ b/drivers/network/dd/nvnet/debug.h @@ -0,0 +1,43 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Debugging support + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +#pragma once + +#define MIN_TRACE 0x00000001 +#define MAX_TRACE 0x00000003 + +#if DBG + +#ifndef NDEBUG + +#define NDIS_DbgPrint(_t_, _x_) \ + do { \ + if (DbgPrint("(%s:%d) %s ", __FILE__, __LINE__, __FUNCTION__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __FILE__, __LINE__); \ + if (DbgPrint _x_) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __FILE__, __LINE__); \ + } while(0) + +#else +#define NDIS_DbgPrint(_t_, _x_) \ + if (_t_ == MAX_TRACE) \ + { \ + if (DbgPrint("(%s:%d) %s ", __FILE__, __LINE__, __FUNCTION__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __FILE__, __LINE__); \ + if (DbgPrint _x_) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __FILE__, __LINE__); \ + } +#endif + +#else + +#define NDIS_DbgPrint(_t_, _x_) + +#define ASSERT_IRQL(x) +#define ASSERT_IRQL_EQUAL(x) + +#endif diff --git a/drivers/network/dd/nvnet/eth.h b/drivers/network/dd/nvnet/eth.h new file mode 100644 index 00000000000..82d7097548d --- /dev/null +++ b/drivers/network/dd/nvnet/eth.h @@ -0,0 +1,57 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Ethernet definitions + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +#pragma once + +#include +typedef struct _ETH_HEADER +{ + UCHAR Destination[ETH_LENGTH_OF_ADDRESS]; + UCHAR Source[ETH_LENGTH_OF_ADDRESS]; + USHORT PayloadType; +} ETH_HEADER, *PETH_HEADER; +#include + +#define ETH_IS_LOCALLY_ADMINISTERED(Address) \ + (BOOLEAN)(((PUCHAR)(Address))[0] & ((UCHAR)0x02)) + +#define ETH_IS_EMPTY(Address) \ + (BOOLEAN)((((PUCHAR)(Address))[0] | ((PUCHAR)(Address))[1] | ((PUCHAR)(Address))[2] | \ + ((PUCHAR)(Address))[3] | ((PUCHAR)(Address))[5] | ((PUCHAR)(Address))[5]) == 0) + +typedef struct IPv4_HEADER +{ + UCHAR VersionLength; + UCHAR Tos; + USHORT TotalLength; + USHORT Id; + USHORT Offset; + UCHAR Ttl; + UCHAR Protocol; + USHORT Checksum; + ULONG Source; + ULONG Destination; +} IPv4_HEADER, *PIPv4_HEADER; + +typedef struct TCPv4_HEADER +{ + USHORT SourcePort; + USHORT DestinationPort; + ULONG SequenceNumber; + ULONG AckNumber; + UCHAR DataOffset; + UCHAR Flags; + USHORT Window; + USHORT Checksum; + USHORT Urgent; +} TCPv4_HEADER, *PTCPv4_HEADER; + +#define IP_HEADER_LENGTH(Header) \ + (((Header)->VersionLength & 0x0F) << 2) + +#define TCP_HEADER_LENGTH(Header) \ + ((Header->DataOffset & 0xF0) >> 2) diff --git a/drivers/network/dd/nvnet/init.c b/drivers/network/dd/nvnet/init.c new file mode 100644 index 00000000000..8964805494c --- /dev/null +++ b/drivers/network/dd/nvnet/init.c @@ -0,0 +1,885 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Miniport initialization helper routines + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "nvnet.h" + +#define NDEBUG +#include "debug.h" + +/* FUNCTIONS ******************************************************************/ + +static +CODE_SEG("PAGE") +VOID +QueryInteger( + _In_ NDIS_HANDLE ConfigurationHandle, + _In_ PCWSTR EntryName, + _Out_ PULONG EntryContext, + _In_ ULONG DefaultValue, + _In_ ULONG Minimum, + _In_ ULONG Maximum) +{ + NDIS_STATUS Status; + UNICODE_STRING Keyword; + PNDIS_CONFIGURATION_PARAMETER ConfigurationParameter; + + PAGED_CODE(); + + NdisInitUnicodeString(&Keyword, EntryName); + NdisReadConfiguration(&Status, + &ConfigurationParameter, + ConfigurationHandle, + &Keyword, + NdisParameterInteger); + if (Status != NDIS_STATUS_SUCCESS) + { + NDIS_DbgPrint(MIN_TRACE, ("'%S' request failed\n", EntryName)); + + *EntryContext = DefaultValue; + } + else + { + if (ConfigurationParameter->ParameterData.IntegerData >= Minimum && + ConfigurationParameter->ParameterData.IntegerData <= Maximum) + { + *EntryContext = ConfigurationParameter->ParameterData.IntegerData; + } + else + { + NDIS_DbgPrint(MAX_TRACE, ("'%S' value out of range\n", EntryName)); + + *EntryContext = DefaultValue; + } + } + + NDIS_DbgPrint(MIN_TRACE, ("Set '%S' to %d\n", EntryName, *EntryContext)); +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +NvNetReadConfiguration( + _Inout_ PNVNET_ADAPTER Adapter) +{ + NDIS_STATUS Status; + NDIS_HANDLE ConfigurationHandle; + PUCHAR NetworkAddress; + UINT Length; + ULONG GenericUlong; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + NdisOpenConfiguration(&Status, + &ConfigurationHandle, + Adapter->WrapperConfigurationHandle); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + QueryInteger(ConfigurationHandle, + L"OptimizationMode", + &GenericUlong, + NV_OPTIMIZATION_MODE_DYNAMIC, + NV_OPTIMIZATION_MODE_THROUGHPUT, + NV_OPTIMIZATION_MODE_DYNAMIC); + Adapter->OptimizationMode = GenericUlong; + + QueryInteger(ConfigurationHandle, + L"FlowControl", + &GenericUlong, + NV_FLOW_CONTROL_AUTO, + NV_FLOW_CONTROL_DISABLE, + NV_FLOW_CONTROL_RX_TX); + Adapter->FlowControlMode = GenericUlong; + + QueryInteger(ConfigurationHandle, + L"SpeedDuplex", + &GenericUlong, + 0, + 0, + 4); + switch (GenericUlong) + { + case 1: + Adapter->Flags |= NV_FORCE_SPEED_AND_DUPLEX; + break; + case 2: + Adapter->Flags |= (NV_FORCE_SPEED_AND_DUPLEX | NV_FORCE_FULL_DUPLEX); + break; + case 3: + Adapter->Flags |= (NV_FORCE_SPEED_AND_DUPLEX | NV_USER_SPEED_100); + break; + case 4: + Adapter->Flags |= (NV_FORCE_SPEED_AND_DUPLEX | NV_FORCE_FULL_DUPLEX | + NV_USER_SPEED_100); + break; + + default: + break; + } + + QueryInteger(ConfigurationHandle, + L"ChecksumOffload", + &GenericUlong, + 0, + 0, + 1); + if (GenericUlong) + Adapter->Flags |= NV_SEND_CHECKSUM; + + QueryInteger(ConfigurationHandle, + L"LargeSendOffload", + &GenericUlong, + 0, + 0, + 1); + if (GenericUlong) + Adapter->Flags |= NV_SEND_LARGE_SEND; + + QueryInteger(ConfigurationHandle, + L"JumboSize", + &GenericUlong, + NVNET_MAXIMUM_FRAME_SIZE, + NVNET_MAXIMUM_FRAME_SIZE, + NVNET_MAXIMUM_FRAME_SIZE_JUMBO); + Adapter->MaximumFrameSize = GenericUlong; + + QueryInteger(ConfigurationHandle, + L"Priority", + &GenericUlong, + 0, + 0, + 1); + if (GenericUlong) + Adapter->Flags |= NV_PACKET_PRIORITY; + + QueryInteger(ConfigurationHandle, + L"VlanTag", + &GenericUlong, + 0, + 0, + 1); + if (GenericUlong) + Adapter->Flags |= NV_VLAN_TAGGING; + + QueryInteger(ConfigurationHandle, + L"VlanID", + &GenericUlong, + 0, + 0, + NVNET_MAXIMUM_VLAN_ID); + + NdisReadNetworkAddress(&Status, + (PVOID*)&NetworkAddress, + &Length, + ConfigurationHandle); + if ((Status == NDIS_STATUS_SUCCESS) && (Length == ETH_LENGTH_OF_ADDRESS)) + { + if ((ETH_IS_MULTICAST(NetworkAddress) || ETH_IS_BROADCAST(NetworkAddress)) || + !ETH_IS_LOCALLY_ADMINISTERED(NetworkAddress)) + { + NDIS_DbgPrint(MAX_TRACE, ("Invalid software MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + NetworkAddress[0], + NetworkAddress[1], + NetworkAddress[2], + NetworkAddress[3], + NetworkAddress[4], + NetworkAddress[5])); + } + else + { + NDIS_DbgPrint(MIN_TRACE, ("Using software MAC\n")); + + ETH_COPY_NETWORK_ADDRESS(Adapter->CurrentMacAddress, NetworkAddress); + + Adapter->Flags |= NV_USE_SOFT_MAC_ADDRESS; + } + } + Status = NDIS_STATUS_SUCCESS; + + NdisCloseConfiguration(ConfigurationHandle); + + return Status; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +NvNetInitializeAdapterResources( + _Inout_ PNVNET_ADAPTER Adapter) +{ + NDIS_STATUS Status; + PNDIS_RESOURCE_LIST AssignedResources = NULL; + UINT i, ResourceListSize = 0; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + NdisMQueryAdapterResources(&Status, + Adapter->WrapperConfigurationHandle, + AssignedResources, + &ResourceListSize); + if (Status != NDIS_STATUS_RESOURCES) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_RESOURCE_CONFLICT); + return NDIS_STATUS_FAILURE; + } + + Status = NdisAllocateMemoryWithTag((PVOID*)&AssignedResources, + ResourceListSize, + NVNET_TAG); + if (Status != NDIS_STATUS_SUCCESS) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_OUT_OF_RESOURCES); + return Status; + } + + NdisMQueryAdapterResources(&Status, + Adapter->WrapperConfigurationHandle, + AssignedResources, + &ResourceListSize); + if (Status != NDIS_STATUS_SUCCESS) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_RESOURCE_CONFLICT); + goto Cleanup; + } + + for (i = 0; i < AssignedResources->Count; ++i) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = &AssignedResources->PartialDescriptors[i]; + + switch (Descriptor->Type) + { + case CmResourceTypeMemory: + { + Adapter->IoAddress = Descriptor->u.Memory.Start; + Adapter->IoLength = Descriptor->u.Memory.Length; + break; + } + + case CmResourceTypeInterrupt: + { + Adapter->InterruptVector = Descriptor->u.Interrupt.Vector; + Adapter->InterruptLevel = Descriptor->u.Interrupt.Level; + Adapter->InterruptShared = (Descriptor->ShareDisposition == CmResourceShareShared); + Adapter->InterruptFlags = Descriptor->Flags; + break; + } + + default: + break; + } + } + + if (!Adapter->IoAddress.QuadPart || !Adapter->InterruptVector) + { + Status = NDIS_STATUS_RESOURCES; + NvNetLogError(Adapter, NDIS_ERROR_CODE_RESOURCE_CONFLICT); + goto Cleanup; + } + + NDIS_DbgPrint(MIN_TRACE, ("MEM at [%I64X-%I64X]\n", + Adapter->IoAddress.QuadPart, + Adapter->IoAddress.QuadPart + Adapter->IoLength)); + NDIS_DbgPrint(MIN_TRACE, ("IRQ Vector %d Level %d\n", + Adapter->InterruptVector, + Adapter->InterruptLevel)); + + Status = NdisMMapIoSpace((PVOID*)&Adapter->IoBase, + Adapter->AdapterHandle, + Adapter->IoAddress, + Adapter->IoLength); + if (Status != NDIS_STATUS_SUCCESS) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_RESOURCE_CONFLICT); + goto Cleanup; + } + +Cleanup: + NdisFreeMemory(AssignedResources, ResourceListSize, 0); + + return Status; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AllocateTransmitBuffers( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG i; + BOOLEAN HasBuffer = FALSE; + PNVNET_TX_BUFFER CoalesceBuffer; + NDIS_STATUS Status; + + PAGED_CODE(); + + Status = NdisAllocateMemoryWithTag((PVOID*)&CoalesceBuffer, + NVNET_TRANSMIT_BUFFERS * sizeof(NVNET_TX_BUFFER), + NVNET_TAG); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + NdisZeroMemory(CoalesceBuffer, NVNET_TRANSMIT_BUFFERS * sizeof(NVNET_TX_BUFFER)); + + Adapter->SendBuffer = CoalesceBuffer; + + for (i = 0; i < NVNET_TRANSMIT_BUFFERS; ++i) + { + PVOID VirtualAddress; + NDIS_PHYSICAL_ADDRESS PhysicalAddress; + + NdisMAllocateSharedMemory(Adapter->AdapterHandle, + Adapter->MaximumFrameSize + NVNET_ALIGNMENT, + TRUE, /* Cached */ + &VirtualAddress, + &PhysicalAddress); + if (!VirtualAddress) + continue; + + CoalesceBuffer->VirtualAddress = ALIGN_UP_POINTER_BY(VirtualAddress, NVNET_ALIGNMENT); + CoalesceBuffer->PhysicalAddress.QuadPart = + ALIGN_UP_BY(PhysicalAddress.QuadPart, NVNET_ALIGNMENT); + + Adapter->SendBufferAllocationData[i].PhysicalAddress.QuadPart = PhysicalAddress.QuadPart; + Adapter->SendBufferAllocationData[i].VirtualAddress = VirtualAddress; + + PushEntryList(&Adapter->Send.BufferList, &CoalesceBuffer->Link); + ++CoalesceBuffer; + + HasBuffer = TRUE; + } + if (!HasBuffer) + { + return NDIS_STATUS_RESOURCES; + } + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AllocateTransmitBlocks( + _In_ PNVNET_ADAPTER Adapter) +{ + PNVNET_TCB Tcb; + NDIS_STATUS Status; + + PAGED_CODE(); + + Status = NdisAllocateMemoryWithTag((PVOID*)&Tcb, + NVNET_TRANSMIT_BLOCKS * sizeof(NVNET_TCB), + NVNET_TAG); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + NdisZeroMemory(Tcb, NVNET_TRANSMIT_BLOCKS * sizeof(NVNET_TCB)); + + Adapter->Send.TailTcb = Tcb + (NVNET_TRANSMIT_BLOCKS - 1); + Adapter->Send.HeadTcb = Tcb; + Adapter->Send.CurrentTcb = Tcb; + Adapter->Send.LastTcb = Tcb; + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AllocateTransmitDescriptors( + _In_ PNVNET_ADAPTER Adapter) +{ + NVNET_TBD Tbd; + ULONG Size; + + PAGED_CODE(); + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + Size = sizeof(NVNET_DESCRIPTOR_64); + } + else + { + Size = sizeof(NVNET_DESCRIPTOR_32); + } + NdisMAllocateSharedMemory(Adapter->AdapterHandle, + Size * NVNET_TRANSMIT_DESCRIPTORS + NVNET_ALIGNMENT, + TRUE, /* Cached */ + &Adapter->TbdOriginal, + &Adapter->TbdPhysOriginal); + if (!Adapter->TbdOriginal) + return NDIS_STATUS_RESOURCES; + + Tbd.Memory = ALIGN_UP_POINTER_BY(Adapter->TbdOriginal, NVNET_ALIGNMENT); + Adapter->TbdPhys.QuadPart = ALIGN_UP_BY(Adapter->TbdPhysOriginal.QuadPart, NVNET_ALIGNMENT); + + Adapter->Send.HeadTbd = Tbd; + Adapter->Send.CurrentTbd = Tbd; + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + Adapter->Send.TailTbd.x64 = Tbd.x64 + (NVNET_TRANSMIT_DESCRIPTORS - 1); + } + else + { + Adapter->Send.TailTbd.x32 = Tbd.x32 + (NVNET_TRANSMIT_DESCRIPTORS - 1); + } + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AllocateReceiveDescriptors( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG Size; + + PAGED_CODE(); + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + Size = sizeof(NVNET_DESCRIPTOR_64); + } + else + { + Size = sizeof(NVNET_DESCRIPTOR_32); + } + NdisMAllocateSharedMemory(Adapter->AdapterHandle, + Size * NVNET_RECEIVE_DESCRIPTORS + NVNET_ALIGNMENT, + TRUE, /* Cached */ + &Adapter->RbdOriginal, + &Adapter->RbdPhysOriginal); + if (!Adapter->RbdOriginal) + return NDIS_STATUS_RESOURCES; + + Adapter->Receive.NvRbd.Memory = ALIGN_UP_POINTER_BY(Adapter->RbdOriginal, NVNET_ALIGNMENT); + Adapter->RbdPhys.QuadPart = ALIGN_UP_BY(Adapter->RbdPhysOriginal.QuadPart, NVNET_ALIGNMENT); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AllocateReceiveBuffers( + _In_ PNVNET_ADAPTER Adapter) +{ + PAGED_CODE(); + + NdisMAllocateSharedMemory(Adapter->AdapterHandle, + NVNET_RECEIVE_BUFFER_SIZE * NVNET_RECEIVE_DESCRIPTORS, + TRUE, /* Cached */ + (PVOID*)&Adapter->ReceiveBuffer, + &Adapter->ReceiveBufferPhys); + if (!Adapter->ReceiveBuffer) + { + return NDIS_STATUS_RESOURCES; + } + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AllocateAdapterMemory( + _In_ PNVNET_ADAPTER Adapter) +{ + NDIS_STATUS Status; + + PAGED_CODE(); + + Status = AllocateTransmitBuffers(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + Status = AllocateTransmitBlocks(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + Status = AllocateTransmitDescriptors(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + Status = AllocateReceiveDescriptors(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + Status = AllocateReceiveBuffers(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + NdisAllocateSpinLock(&Adapter->Send.Lock); + NdisAllocateSpinLock(&Adapter->Receive.Lock); + NdisAllocateSpinLock(&Adapter->Lock); + + return NDIS_STATUS_SUCCESS; +} + +CODE_SEG("PAGE") +VOID +NvNetInitTransmitMemory( + _In_ PNVNET_ADAPTER Adapter) +{ + PAGED_CODE(); + + Adapter->Send.TcbSlots = NVNET_TRANSMIT_BLOCKS; + Adapter->Send.TbdSlots = NVNET_TRANSMIT_DESCRIPTORS; + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + NdisZeroMemory(Adapter->Send.HeadTbd.x64, + sizeof(NVNET_DESCRIPTOR_64) * NVNET_TRANSMIT_DESCRIPTORS); + } + else + { + NdisZeroMemory(Adapter->Send.HeadTbd.x32, + sizeof(NVNET_DESCRIPTOR_32) * NVNET_TRANSMIT_DESCRIPTORS); + } +} + +static +CODE_SEG("PAGE") +VOID +NvNetInitReceiveMemory( + _In_ PNVNET_ADAPTER Adapter) +{ + NV_RBD NvRbd; + ULONG i; + + PAGED_CODE(); + + Adapter->CurrentRx = 0; + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + for (i = 0; i < NVNET_RECEIVE_DESCRIPTORS; ++i) + { + NvRbd.x64 = &Adapter->Receive.NvRbd.x64[i]; + + NvRbd.x64->AddressHigh = NdisGetPhysicalAddressHigh(Adapter->ReceiveBufferPhys) + + i * NVNET_RECEIVE_BUFFER_SIZE; + NvRbd.x64->AddressLow = NdisGetPhysicalAddressLow(Adapter->ReceiveBufferPhys) + + i * NVNET_RECEIVE_BUFFER_SIZE; + NvRbd.x64->VlanTag = 0; + NvRbd.x64->FlagsLength = NV_RX2_AVAIL | NVNET_RECEIVE_BUFFER_SIZE; + } + } + else + { + for (i = 0; i < NVNET_RECEIVE_DESCRIPTORS; ++i) + { + NvRbd.x32 = &Adapter->Receive.NvRbd.x32[i]; + + NvRbd.x32->Address = NdisGetPhysicalAddressLow(Adapter->ReceiveBufferPhys) + + i * NVNET_RECEIVE_BUFFER_SIZE; + NvRbd.x32->FlagsLength = NV_RX_AVAIL | NVNET_RECEIVE_BUFFER_SIZE; + } + } +} + + +CODE_SEG("PAGE") +VOID +NvNetFreeAdapter( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG i; + ULONG DescriptorSize; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + for (i = 0; i < RTL_NUMBER_OF(Adapter->WakeFrames); ++i) + { + PNVNET_WAKE_FRAME WakeFrame = Adapter->WakeFrames[i]; + + if (!WakeFrame) + continue; + + NdisFreeMemory(WakeFrame, sizeof(*WakeFrame), 0); + } + + if (Adapter->Interrupt.InterruptObject) + { + NdisMDeregisterInterrupt(&Adapter->Interrupt); + Adapter->Interrupt.InterruptObject = NULL; + } + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + DescriptorSize = sizeof(NVNET_DESCRIPTOR_64); + } + else + { + DescriptorSize = sizeof(NVNET_DESCRIPTOR_32); + } + if (Adapter->TbdOriginal) + { + NdisMFreeSharedMemory(Adapter->AdapterHandle, + DescriptorSize * NVNET_TRANSMIT_DESCRIPTORS, + FALSE, + Adapter->TbdOriginal, + Adapter->TbdPhysOriginal); + Adapter->TbdOriginal = NULL; + } + if (Adapter->RbdOriginal) + { + NdisMFreeSharedMemory(Adapter->AdapterHandle, + DescriptorSize * NVNET_RECEIVE_DESCRIPTORS, + FALSE, + Adapter->RbdOriginal, + Adapter->RbdPhysOriginal); + Adapter->RbdOriginal = NULL; + } + if (Adapter->SendBuffer) + { + ULONG Length = ALIGN_UP_BY(Adapter->MaximumFrameSize, NVNET_ALIGNMENT); + + for (i = 0; i < NVNET_TRANSMIT_BUFFERS; ++i) + { + PNVNET_TX_BUFFER_DATA SendBufferData = &Adapter->SendBufferAllocationData[i]; + + if (!SendBufferData->VirtualAddress) + continue; + + NdisMFreeSharedMemory(Adapter->AdapterHandle, + Length, + TRUE, + SendBufferData->VirtualAddress, + SendBufferData->PhysicalAddress); + } + + NdisFreeMemory(Adapter->SendBuffer, NVNET_TRANSMIT_BUFFERS * sizeof(NVNET_TX_BUFFER), 0); + Adapter->SendBuffer = NULL; + } + + if (Adapter->ReceiveBuffer) + { + NdisMFreeSharedMemory(Adapter->AdapterHandle, + NVNET_RECEIVE_BUFFER_SIZE * NVNET_RECEIVE_DESCRIPTORS, + FALSE, + Adapter->ReceiveBuffer, + Adapter->ReceiveBufferPhys); + Adapter->ReceiveBuffer = NULL; + } + + if (Adapter->IoBase) + { + NdisMUnmapIoSpace(Adapter->AdapterHandle, + Adapter->IoBase, + Adapter->IoLength); + Adapter->IoBase = NULL; + } + + if (Adapter->Lock.SpinLock) + NdisFreeSpinLock(&Adapter->Lock); + if (Adapter->Send.Lock.SpinLock) + NdisFreeSpinLock(&Adapter->Send.Lock); + if (Adapter->Receive.Lock.SpinLock) + NdisFreeSpinLock(&Adapter->Receive.Lock); + + NdisFreeMemory(Adapter->AdapterOriginal, sizeof(NVNET_ADAPTER), 0); +} + +CODE_SEG("PAGE") +NDIS_STATUS +NTAPI +MiniportInitialize( + _Out_ PNDIS_STATUS OpenErrorStatus, + _Out_ PUINT SelectedMediumIndex, + _In_ PNDIS_MEDIUM MediumArray, + _In_ UINT MediumArraySize, + _In_ NDIS_HANDLE MiniportAdapterHandle, + _In_ NDIS_HANDLE WrapperConfigurationContext) +{ + UINT i; + ULONG Size; + PVOID UnalignedAdapter; + PNVNET_ADAPTER Adapter; + NDIS_STATUS Status; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + for (i = 0; i < MediumArraySize; ++i) + { + if (MediumArray[i] == NdisMedium802_3) + { + *SelectedMediumIndex = i; + break; + } + } + if (i == MediumArraySize) + { + NDIS_DbgPrint(MAX_TRACE, ("No supported media\n")); + return NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + Size = sizeof(NVNET_ADAPTER) + NdisGetSharedDataAlignment(); + + Status = NdisAllocateMemoryWithTag((PVOID*)&UnalignedAdapter, + Size, + NVNET_TAG); + if (Status != NDIS_STATUS_SUCCESS) + { + NDIS_DbgPrint(MAX_TRACE, ("Failed to allocate adapter\n")); + return NDIS_STATUS_RESOURCES; + } + + NdisZeroMemory(UnalignedAdapter, Size); + Adapter = ALIGN_UP_POINTER_BY(UnalignedAdapter, NdisGetSharedDataAlignment()); + Adapter->AdapterOriginal = UnalignedAdapter; + Adapter->AdapterHandle = MiniportAdapterHandle; + Adapter->WrapperConfigurationHandle = WrapperConfigurationContext; + + Status = NvNetReadConfiguration(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + { + goto Failure; + } + + NdisMSetAttributesEx(MiniportAdapterHandle, + Adapter, + 0, + NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS | + // NDIS_ATTRIBUTE_DESERIALIZE | TODO + NDIS_ATTRIBUTE_BUS_MASTER, + NdisInterfacePci); + + Status = NvNetRecognizeHardware(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + { + if (Status == NDIS_STATUS_ADAPTER_NOT_FOUND) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_ADAPTER_NOT_FOUND); + } + else if (Status == NDIS_STATUS_NOT_RECOGNIZED) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_UNSUPPORTED_CONFIGURATION); + } + + goto Failure; + } + + Status = NvNetInitializeAdapterResources(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + { + goto Failure; + } + + Status = NdisMInitializeScatterGatherDma(Adapter->AdapterHandle, + !!(Adapter->Features & DEV_HAS_HIGH_DMA), + NVNET_MAXIMUM_FRAME_SIZE); + // ^TODO: NVNET_MAX_DMA_TRANSFER); + if (Status != NDIS_STATUS_SUCCESS) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_OUT_OF_RESOURCES); + goto Failure; + } + + Status = AllocateAdapterMemory(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + { + NDIS_DbgPrint(MAX_TRACE, ("Failed to allocate adapter memory\n")); + + NvNetLogError(Adapter, NDIS_ERROR_CODE_OUT_OF_RESOURCES); + goto Failure; + } + + NvNetInitTransmitMemory(Adapter); + NvNetInitReceiveMemory(Adapter); + + if (Adapter->Features & DEV_HAS_HIGH_DMA) + { + Adapter->TransmitPacket = NvNetTransmitPacket64; + Adapter->ProcessTransmit = ProcessTransmitDescriptors64; + } + else + { + Adapter->TransmitPacket = NvNetTransmitPacket32; + + if (Adapter->Features & DEV_HAS_LARGEDESC) + { + Adapter->ProcessTransmit = ProcessTransmitDescriptors32; + } + else + { + Adapter->ProcessTransmit = ProcessTransmitDescriptorsLegacy; + } + } + + Status = NvNetGetPermanentMacAddress(Adapter, Adapter->PermanentMacAddress); + if (Status != NDIS_STATUS_SUCCESS) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_NETWORK_ADDRESS); + goto Failure; + } + + if (!(Adapter->Flags & NV_USE_SOFT_MAC_ADDRESS)) + { + ETH_COPY_NETWORK_ADDRESS(Adapter->CurrentMacAddress, + Adapter->PermanentMacAddress); + } + + NvNetSetupMacAddress(Adapter, Adapter->CurrentMacAddress); + + Status = NvNetInitNIC(Adapter, TRUE); + if (Status != NDIS_STATUS_SUCCESS) + { + NDIS_DbgPrint(MAX_TRACE, ("Failed to initialize the NIC\n")); + + NvNetLogError(Adapter, NDIS_ERROR_CODE_HARDWARE_FAILURE); + goto Failure; + } + + NvNetDisableInterrupts(Adapter); + NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_ALL); + NV_WRITE(Adapter, NvRegIrqStatus, NVREG_IRQSTAT_MASK); + +/* FIXME: Bug in the PIC HAL? */ +#if defined(SARCH_XBOX) + Status = NdisMRegisterInterrupt(&Adapter->Interrupt, + Adapter->AdapterHandle, + Adapter->InterruptVector, + Adapter->InterruptLevel, + TRUE, /* Request ISR calls */ + FALSE, + NdisInterruptLatched); +#else + Status = NdisMRegisterInterrupt(&Adapter->Interrupt, + Adapter->AdapterHandle, + Adapter->InterruptVector, + Adapter->InterruptLevel, + TRUE, /* Request ISR calls */ + TRUE, /* Shared */ + NdisInterruptLevelSensitive); +#endif + if (Status != NDIS_STATUS_SUCCESS) + { + NvNetLogError(Adapter, NDIS_ERROR_CODE_INTERRUPT_CONNECT); + goto Failure; + } + + NvNetStartAdapter(Adapter); + + return NDIS_STATUS_SUCCESS; + +Failure: + NvNetFreeAdapter(Adapter); + + return Status; +} diff --git a/drivers/network/dd/nvnet/interrupt.c b/drivers/network/dd/nvnet/interrupt.c new file mode 100644 index 00000000000..2d8a3df867e --- /dev/null +++ b/drivers/network/dd/nvnet/interrupt.c @@ -0,0 +1,549 @@ +/* + * 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 + */ + +/* 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; + } +} diff --git a/drivers/network/dd/nvnet/netnv.inf b/drivers/network/dd/nvnet/netnv.inf new file mode 100644 index 00000000000..9e1aafd4fee --- /dev/null +++ b/drivers/network/dd/nvnet/netnv.inf @@ -0,0 +1,288 @@ +; NETNV.INF + +; Installation file for nForce-based NICs + +[Version] +Signature = "$Windows NT$" +;Signature = "$ReactOS$" +LayoutFile = layout.inf +Class = Net +ClassGUID = {4D36E972-E325-11CE-BFC1-08002BE10318} +Provider = %ReactOS% +DriverVer = 08/01/2021,1.00 + +[DestinationDirs] +DefaultDestDir = 12 + +[Manufacturer] +%nVidiaMfg% = nVidiaMfg + +[ControlFlags] +ExcludeFromSelect = * + +[nVidiaMfg] +%NVNET.DeviceDesc% = NVNET_Inst_V1.ndi,PCI\VEN_10DE&DEV_01C3 +%NVNET.DeviceDesc% = NVNET_Inst_V1.ndi,PCI\VEN_10DE&DEV_0066 +%NVNET.DeviceDesc% = NVNET_Inst_V1.ndi,PCI\VEN_10DE&DEV_00D6 +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_0086 +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_008C +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_00E6 +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_00DF +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_0056 +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_0057 +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_0037 +%NVNET.DeviceDesc% = NVNET_Inst_V2.ndi,PCI\VEN_10DE&DEV_0038 +%NVNET.DeviceDesc% = NVNET_Inst_V1.ndi,PCI\VEN_10DE&DEV_0268 +%NVNET.DeviceDesc% = NVNET_Inst_V1.ndi,PCI\VEN_10DE&DEV_0269 +%NVNET.DeviceDesc% = NVNET_Inst_V3.ndi,PCI\VEN_10DE&DEV_0372 +%NVNET.DeviceDesc% = NVNET_Inst_V3.ndi,PCI\VEN_10DE&DEV_0373 +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_03E5 +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_03E6 +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_03EE +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_03EF +%NVNET.DeviceDesc% = NVNET_Inst_V5.ndi,PCI\VEN_10DE&DEV_0450 +%NVNET.DeviceDesc% = NVNET_Inst_V5.ndi,PCI\VEN_10DE&DEV_0451 +%NVNET.DeviceDesc% = NVNET_Inst_V5.ndi,PCI\VEN_10DE&DEV_0452 +%NVNET.DeviceDesc% = NVNET_Inst_V5.ndi,PCI\VEN_10DE&DEV_0453 +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_054C +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_054D +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_054E +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_054F +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_07DC +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_07DD +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_07DE +%NVNET.DeviceDesc% = NVNET_Inst_V4.ndi,PCI\VEN_10DE&DEV_07DF +%NVNET.DeviceDesc% = NVNET_Inst_V6.ndi,PCI\VEN_10DE&DEV_0760 +%NVNET.DeviceDesc% = NVNET_Inst_V6.ndi,PCI\VEN_10DE&DEV_0761 +%NVNET.DeviceDesc% = NVNET_Inst_V6.ndi,PCI\VEN_10DE&DEV_0762 +%NVNET.DeviceDesc% = NVNET_Inst_V6.ndi,PCI\VEN_10DE&DEV_0763 +%NVNET.DeviceDesc% = NVNET_Inst_V7.ndi,PCI\VEN_10DE&DEV_0AB0 +%NVNET.DeviceDesc% = NVNET_Inst_V7.ndi,PCI\VEN_10DE&DEV_0AB1 +%NVNET.DeviceDesc% = NVNET_Inst_V7.ndi,PCI\VEN_10DE&DEV_0AB2 +%NVNET.DeviceDesc% = NVNET_Inst_V7.ndi,PCI\VEN_10DE&DEV_0AB3 +%NVNET.DeviceDesc% = NVNET_Inst_V7.ndi,PCI\VEN_10DE&DEV_0D7D + +;----------------------------- NVNET DRIVER ----------------------------- + +[NVNET_Inst_V1.ndi.NT] +Characteristics = 0x84 ; NCF_PHYSICAL | NCF_HAS_UI +BusType = 5 ; PCIBus +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow + +[NVNET_Inst_V2.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow, NVNET_Jumbo, NVNET_Offload + +[NVNET_Inst_V3.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow_V2, NVNET_Jumbo, NVNET_Offload, NVNET_VLAN + +[NVNET_Inst_V4.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow_V2 + +[NVNET_Inst_V5.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow_V2, NVNET_Jumbo + +[NVNET_Inst_V6.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow_V2, NVNET_Offload + +[NVNET_Inst_V7.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = NVNET_CopyFiles.NT +AddReg = NVNET_AddReg, NVNET_Generic, NVNET_Flow_V2, NVNET_Jumbo, NVNET_Offload + +[NVNET_CopyFiles.NT] +nvnet.sys + +[NVNET_Inst_V1.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_Inst_V2.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_Inst_V3.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_Inst_V4.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_Inst_V5.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_Inst_V6.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_Inst_V7.ndi.NT.Services] +AddService = nvnet, 2, NVNET_Service_Inst, NVNET_EventLog + +[NVNET_AddReg] +HKR, Ndi, Service, 0, "nvnet" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +[NVNET_Generic] +HKR, Ndi\params\OptimizationMode, ParamDesc, 0, %OM% +HKR, Ndi\params\OptimizationMode, type, 0, "enum" +HKR, Ndi\params\OptimizationMode, default, 0, "0" +HKR, Ndi\params\OptimizationMode\enum, "0", 0, "%M1%" +HKR, Ndi\params\OptimizationMode\enum, "1", 0, "%M2%" +HKR, Ndi\params\OptimizationMode\enum, "2", 0, "%M3%" + +HKR, Ndi\params\NetworkAddress, ParamDesc, 0, %NA% +HKR, Ndi\params\NetworkAddress, type, 0, "edit" +HKR, Ndi\params\NetworkAddress, LimitText, 0, "12" +HKR, Ndi\params\NetworkAddress, UpperCase, 0, "1" +HKR, Ndi\params\NetworkAddress, default, 0, " " +HKR, Ndi\params\NetworkAddress, optional, 0, "1" + +HKR, Ndi\params\SpeedDuplex, ParamDesc, 0, %SD% +HKR, Ndi\params\SpeedDuplex, type, 0, "enum" +HKR, Ndi\params\SpeedDuplex, default, 0, "0" +HKR, Ndi\params\SpeedDuplex\enum, "0", 0, "%Auto%" +HKR, Ndi\params\SpeedDuplex\enum, "1", 0, "10HD" +HKR, Ndi\params\SpeedDuplex\enum, "2", 0, "10FD" +HKR, Ndi\params\SpeedDuplex\enum, "3", 0, "100HD" +HKR, Ndi\params\SpeedDuplex\enum, "4", 0, "100FD" + +[NVNET_Flow] +HKR, Ndi\params\FlowControl, ParamDesc, 0, %FC% +HKR, Ndi\params\FlowControl, type, 0, "enum" +HKR, Ndi\params\FlowControl, default, 0, "1" +HKR, Ndi\params\FlowControl\enum, "0", 0, "%No%" +HKR, Ndi\params\FlowControl\enum, "1", 0, "%Auto%" +HKR, Ndi\params\FlowControl\enum, "2", 0, "%FlowRx%" + +[NVNET_Flow_V2] +HKR, Ndi\params\FlowControl, ParamDesc, 0, %FC% +HKR, Ndi\params\FlowControl, type, 0, "enum" +HKR, Ndi\params\FlowControl, default, 0, "1" +HKR, Ndi\params\FlowControl\enum, "0", 0, "%No%" +HKR, Ndi\params\FlowControl\enum, "1", 0, "%Auto%" +HKR, Ndi\params\FlowControl\enum, "2", 0, "%FRx%" +HKR, Ndi\params\FlowControl\enum, "3", 0, "%FTx%" +HKR, Ndi\params\FlowControl\enum, "4", 0, "%FBoth%" + +[NVNET_Offload] +HKR, Ndi\params\ChecksumOffload, ParamDesc, 0, %CO% +HKR, Ndi\params\ChecksumOffload, type, 0, "enum" +HKR, Ndi\params\ChecksumOffload, default, 0, "1" +HKR, Ndi\params\ChecksumOffload\enum, "0", 0, "%No%" +HKR, Ndi\params\ChecksumOffload\enum, "1", 0, "%Yes%" + +HKR, Ndi\params\LargeSendOffload, ParamDesc, 0, %LS% +HKR, Ndi\params\LargeSendOffload, type, 0, "enum" +HKR, Ndi\params\LargeSendOffload, default, 0, "1" +HKR, Ndi\params\LargeSendOffload\enum, "0", 0, "%No%" +HKR, Ndi\params\LargeSendOffload\enum, "1", 0, "%Yes%" + +[NVNET_Jumbo] +HKR, Ndi\params\JumboSize, ParamDesc, 0, %JF% +HKR, Ndi\params\JumboSize, type, 0, "int" +HKR, Ndi\params\JumboSize, default, 0, "1514" +HKR, Ndi\params\JumboSize, min, 0, "1514" +HKR, Ndi\params\JumboSize, max, 0, "9014" +HKR, Ndi\params\JumboSize, step, 0, "1" +HKR, Ndi\params\JumboSize, base, 0, "10" + +[NVNET_VLAN] +HKR, Ndi\params\Priority, ParamDesc, 0, %PP% +HKR, Ndi\params\Priority, type, 0, "enum" +HKR, Ndi\params\Priority, default, 0, "1" +HKR, Ndi\params\Priority\enum, "0", 0, "%No%" +HKR, Ndi\params\Priority\enum, "1", 0, "%Yes%" + +HKR, Ndi\params\VlanTag, ParamDesc, 0, %VT% +HKR, Ndi\params\VlanTag, type, 0, "enum" +HKR, Ndi\params\VlanTag, default, 0, "1" +HKR, Ndi\params\VlanTag\enum, "0", 0, "%No%" +HKR, Ndi\params\VlanTag\enum, "1", 0, "%Yes%" + +HKR, Ndi\params\VlanID, ParamDesc, 0, %VI% +HKR, Ndi\params\VlanID, type, 0, "long" +HKR, Ndi\params\VlanID, default, 0, "0" +HKR, Ndi\params\VlanID, min, 0, "0" +HKR, Ndi\params\VlanID, max, 0, "4095" +HKR, Ndi\params\VlanID, step, 0, "1" +HKR, Ndi\params\VlanID, base, 0, "10" + +[NVNET_Service_Inst] +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\nvnet.sys +LoadOrderGroup = NDIS + +[NVNET_EventLog] +AddReg = NVNET_EventLog_AddReg + +[NVNET_EventLog_AddReg] +HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x00010001, 7 + +;-------------------------------- STRINGS ------------------------------- + +[Strings] +ReactOS = "ReactOS Team" + +nVidiaMfg = "nVidia" + +OM = "Optimization Mode" +M1 = "Dynamic" +M2 = "CPU" +M3 = "Throughput" +NA = "Network Address" +SD = "Speed & Duplex" +Auto = "Auto" +CO = "Checksum Offload" +LS = "Large Send Offload" +FC = "Flow Control" +FRx = "Rx Pause" +FTx = "Tx Pause" +FBoth = "Rx & Tx Pause" +No = "No" +Yes = "Yes" +PP = "Packet Priority" +VT = "VLAN Tagging" +VI = "VLAN ID" +JF = "Jumbo Frame" + +NVNET.DeviceDesc = "nVidia nForce PCI Ethernet Controller" + +[Strings.0419] +ReactOS = "Команда ReactOS" + +OM = "Оптимизировать" +M1 = "Динамически" +M2 = "Нагрузку на ЦП" +M3 = "Пропускную способность" +NA = "Сетевой адрес" +SD = "Скорость и дуплекс" +Auto = "Авто" +CO = "Разгрузка контрольной суммы" +LS = "Разгрузка при большой отправке" +FC = "Управление потоком" +FRx = "Кадры паузы приема" +FTx = "Кадры паузы передачи" +FBoth = "Кадры паузы приема и передачи" +No = "Нет" +Yes = "Да" +PP = "Приоритет кадров" +VT = "Маркировка кадров" +JF = "Кадры большого размера" + +NVNET.DeviceDesc = "nVidia nForce PCI сетевой контроллер" diff --git a/drivers/network/dd/nvnet/nic.c b/drivers/network/dd/nvnet/nic.c new file mode 100644 index 00000000000..88e7a29e3ee --- /dev/null +++ b/drivers/network/dd/nvnet/nic.c @@ -0,0 +1,927 @@ +/* + * 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; +} diff --git a/drivers/network/dd/nvnet/nic.h b/drivers/network/dd/nvnet/nic.h new file mode 100644 index 00000000000..9b4e1ec4613 --- /dev/null +++ b/drivers/network/dd/nvnet/nic.h @@ -0,0 +1,567 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Hardware specific definitions + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +/* + * Definitions were 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 + */ + +#pragma once + +#define DEV_NEED_TIMERIRQ 0x00000001 /* Set the timer IRQ flag in the IRQ mask */ +#define DEV_NEED_LINKTIMER 0x00000002 /* Poll link settings. Relies on the timer IRQ */ +#define DEV_HAS_LARGEDESC 0x00000004 /* Device supports Jumbo Frames + * and needs packet format 2 */ +#define DEV_HAS_HIGH_DMA 0x00000008 /* Device supports 64-bit DMA */ +#define DEV_HAS_CHECKSUM 0x00000010 /* Device supports TX and RX checksum offloads */ +#define DEV_HAS_VLAN 0x00000020 /* Device supports VLAN tagging and striping */ +#define DEV_HAS_MSI 0x00000040 /* Device supports MSI */ +#define DEV_HAS_MSI_X 0x00000080 /* Device supports MSI-X */ +#define DEV_HAS_POWER_CNTRL 0x00000100 /* Device supports power savings */ +#define DEV_HAS_STATISTICS_V1 0x00000200 /* Device supports HW statistics version 1 */ +#define DEV_HAS_STATISTICS_V2 0x00000400 /* Device supports HW statistics version 2 */ +#define DEV_HAS_STATISTICS_V3 0x00000800 /* Device supports HW statistics version 3 */ +#define DEV_HAS_TEST_EXTENDED 0x00001000 /* Device supports extended diagnostic test */ +#define DEV_HAS_MGMT_UNIT 0x00002000 /* Device supports management unit */ +#define DEV_HAS_CORRECT_MACADDR 0x00004000 /* Device supports correct MAC address order */ +#define DEV_HAS_COLLISION_FIX 0x00008000 /* Device supports TX collision fix */ +#define DEV_HAS_PAUSEFRAME_TX_V1 0x00010000 /* Device supports TX pause frames version 1 */ +#define DEV_HAS_PAUSEFRAME_TX_V2 0x00020000 /* Device supports TX pause frames version 2 */ +#define DEV_HAS_PAUSEFRAME_TX_V3 0x00040000 /* Device supports TX pause frames version 3 */ +#define DEV_NEED_TX_LIMIT 0x00080000 /* Device needs to limit TX */ +#define DEV_NEED_TX_LIMIT2 0x00100000 /* Device needs to limit TX, expect for some revs */ +#define DEV_HAS_GEAR_MODE 0x00200000 /* Device supports gear mode */ +#define DEV_NEED_PHY_INIT_FIX 0x00400000 /* Device needs specific PHY workaround */ +#define DEV_NEED_LOW_POWER_FIX 0x00800000 /* Device needs special power up workaround */ +#define DEV_NEED_MSI_FIX 0x01000000 /* Device needs MSI workaround */ + +#define DEV_HAS_STATISTICS_COUNTERS (DEV_HAS_STATISTICS_V1 | DEV_HAS_STATISTICS_V2 | \ + DEV_HAS_STATISTICS_V3) +#define DEV_HAS_TX_PAUSEFRAME (DEV_HAS_PAUSEFRAME_TX_V1 | DEV_HAS_PAUSEFRAME_TX_V2 | \ + DEV_HAS_PAUSEFRAME_TX_V3) + +typedef enum _NVNET_REGISTER +{ + NvRegIrqStatus = 0x000, +#define NVREG_IRQSTAT_MIIEVENT 0x040 +#define NVREG_IRQSTAT_MASK 0x83ff + + NvRegIrqMask = 0x004, +#define NVREG_IRQ_RX_ERROR 0x0001 +#define NVREG_IRQ_RX 0x0002 +#define NVREG_IRQ_RX_NOBUF 0x0004 +#define NVREG_IRQ_TX_ERR 0x0008 +#define NVREG_IRQ_TX_OK 0x0010 +#define NVREG_IRQ_TIMER 0x0020 +#define NVREG_IRQ_LINK 0x0040 +#define NVREG_IRQ_RX_FORCED 0x0080 +#define NVREG_IRQ_TX_FORCED 0x0100 +#define NVREG_IRQ_RECOVER_ERROR 0x8200 +#define NVREG_IRQMASK_THROUGHPUT 0x00df +#define NVREG_IRQMASK_CPU 0x0060 +#define NVREG_IRQ_TX_ALL (NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED) +#define NVREG_IRQ_RX_ALL (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF| \ + NVREG_IRQ_RX_FORCED) +#define NVREG_IRQ_OTHER (NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RECOVER_ERROR) + + NvRegUnknownSetupReg6 = 0x008, +#define NVREG_UNKSETUP6_VAL 3 + + NvRegPollingInterval = 0x00c, +#define NVREG_POLL_DEFAULT_THROUGHPUT 65535 +#define NVREG_POLL_DEFAULT_CPU 13 + + NvRegMSIMap0 = 0x020, + NvRegMSIMap1 = 0x024, + + NvRegMSIIrqMask = 0x030, +#define NVREG_MSI_VECTOR_0_ENABLED 0x01 + + NvRegMacReset = 0x34, +#define NVREG_MAC_RESET_ASSERT 0x0F3 + + NvRegMisc1 = 0x080, +#define NVREG_MISC1_PAUSE_TX 0x01 +#define NVREG_MISC1_HD 0x02 +#define NVREG_MISC1_FORCE 0x3b0f3c + + NvRegTransmitterControl = 0x084, +#define NVREG_XMITCTL_START 0x01 +#define NVREG_XMITCTL_MGMT_ST 0x40000000 +#define NVREG_XMITCTL_SYNC_MASK 0x000f0000 +#define NVREG_XMITCTL_SYNC_NOT_READY 0x0 +#define NVREG_XMITCTL_SYNC_PHY_INIT 0x00040000 +#define NVREG_XMITCTL_MGMT_SEMA_MASK 0x00000f00 +#define NVREG_XMITCTL_MGMT_SEMA_FREE 0x0 +#define NVREG_XMITCTL_HOST_SEMA_MASK 0x0000f000 +#define NVREG_XMITCTL_HOST_SEMA_ACQ 0x0000f000 +#define NVREG_XMITCTL_HOST_LOADED 0x00004000 +#define NVREG_XMITCTL_TX_PATH_EN 0x01000000 +#define NVREG_XMITCTL_DATA_START 0x00100000 +#define NVREG_XMITCTL_DATA_READY 0x00010000 +#define NVREG_XMITCTL_DATA_ERROR 0x00020000 + + NvRegTransmitterStatus = 0x088, +#define NVREG_XMITSTAT_BUSY 0x01 + + NvRegPacketFilterFlags = 0x8c, +#define NVREG_PFF_PAUSE_RX 0x08 +#define NVREG_PFF_ALWAYS 0x7F0000 +#define NVREG_PFF_PROMISC 0x80 +#define NVREG_PFF_MYADDR 0x20 +#define NVREG_PFF_LOOPBACK 0x10 + + NvRegOffloadConfig = 0x90, +#define NVREG_OFFLOAD_HOMEPHY 0x601 +#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE + + NvRegReceiverControl = 0x094, +#define NVREG_RCVCTL_START 0x01 +#define NVREG_RCVCTL_RX_PATH_EN 0x01000000 + + NvRegReceiverStatus = 0x98, +#define NVREG_RCVSTAT_BUSY 0x01 + + NvRegSlotTime = 0x9c, +#define NVREG_SLOTTIME_LEGBF_ENABLED 0x80000000 +#define NVREG_SLOTTIME_10_100_FULL 0x00007f00 +#define NVREG_SLOTTIME_1000_FULL 0x0003ff00 +#define NVREG_SLOTTIME_HALF 0x0000ff00 +#define NVREG_SLOTTIME_DEFAULT 0x00007f00 +#define NVREG_SLOTTIME_MASK 0x000000ff + + NvRegTxDeferral = 0xA0, +#define NVREG_TX_DEFERRAL_DEFAULT 0x15050f +#define NVREG_TX_DEFERRAL_RGMII_10_100 0x16070f +#define NVREG_TX_DEFERRAL_RGMII_1000 0x14050f +#define NVREG_TX_DEFERRAL_RGMII_STRETCH_10 0x16190f +#define NVREG_TX_DEFERRAL_RGMII_STRETCH_100 0x16300f +#define NVREG_TX_DEFERRAL_MII_STRETCH 0x152000 + + NvRegRxDeferral = 0xA4, +#define NVREG_RX_DEFERRAL_DEFAULT 0x16 + + NvRegMacAddrA = 0xA8, + NvRegMacAddrB = 0xAC, + + NvRegMulticastAddrA = 0xB0, + NvRegMulticastAddrB = 0xB4, +#define NVREG_MCASTADDRA_FORCE 0x01 + + NvRegMulticastMaskA = 0xB8, +#define NVREG_MCASTMASKA_NONE 0xffffffff + + NvRegMulticastMaskB = 0xBC, +#define NVREG_MCASTMASKB_NONE 0xffff + + NvRegPhyInterface = 0xC0, +#define PHY_100 0x1 +#define PHY_1000 0x2 +#define PHY_HALF 0x100 +#define PHY_RGMII 0x10000000 + + NvRegBackOffControl = 0xC4, +#define NVREG_BKOFFCTRL_DEFAULT 0x70000000 +#define NVREG_BKOFFCTRL_SEED_MASK 0x000003ff +#define NVREG_BKOFFCTRL_SELECT 24 +#define NVREG_BKOFFCTRL_GEAR 12 + + NvRegTxRingPhysAddr = 0x100, + NvRegRxRingPhysAddr = 0x104, + + NvRegRingSizes = 0x108, +#define NVREG_RINGSZ_TXSHIFT 0 +#define NVREG_RINGSZ_RXSHIFT 16 + + NvRegTransmitPoll = 0x10c, +#define NVREG_TRANSMITPOLL_MAC_ADDR_REV 0x00008000 + + NvRegLinkSpeed = 0x110, +#define NVREG_LINKSPEED_FORCE 0x10000 +#define NVREG_LINKSPEED_10 1000 +#define NVREG_LINKSPEED_100 100 +#define NVREG_LINKSPEED_1000 50 +#define NVREG_LINKSPEED_MASK (0xFFF) + + NvRegUnknownSetupReg5 = 0x130, +#define NVREG_UNKSETUP5_BIT31 (1<<31) + + NvRegTxWatermark = 0x13c, +#define NVREG_TX_WM_DESC1_DEFAULT 0x0200010 +#define NVREG_TX_WM_DESC2_3_DEFAULT 0x1e08000 +#define NVREG_TX_WM_DESC2_3_1000 0xfe08000 + + NvRegTxRxControl = 0x144, +#define NVREG_TXRXCTL_KICK 0x0001 +#define NVREG_TXRXCTL_BIT1 0x0002 +#define NVREG_TXRXCTL_BIT2 0x0004 +#define NVREG_TXRXCTL_IDLE 0x0008 +#define NVREG_TXRXCTL_RESET 0x0010 +#define NVREG_TXRXCTL_RXCHECK 0x0400 +#define NVREG_TXRXCTL_DESC_1 0 +#define NVREG_TXRXCTL_DESC_2 0x002100 +#define NVREG_TXRXCTL_DESC_3 0xc02200 +#define NVREG_TXRXCTL_VLANSTRIP 0x00040 +#define NVREG_TXRXCTL_VLANINS 0x00080 + + NvRegTxRingPhysAddrHigh = 0x148, + NvRegRxRingPhysAddrHigh = 0x14C, + + NvRegTxPauseFrame = 0x170, +#define NVREG_TX_PAUSEFRAME_DISABLE 0x0fff0080 +#define NVREG_TX_PAUSEFRAME_ENABLE_V1 0x01800010 +#define NVREG_TX_PAUSEFRAME_ENABLE_V2 0x056003f0 +#define NVREG_TX_PAUSEFRAME_ENABLE_V3 0x09f00880 + + NvRegTxPauseFrameLimit = 0x174, +#define NVREG_TX_PAUSEFRAMELIMIT_ENABLE 0x00010000 + + NvRegMIIStatus = 0x180, +#define NVREG_MIISTAT_ERROR 0x0001 +#define NVREG_MIISTAT_LINKCHANGE 0x0008 +#define NVREG_MIISTAT_MASK_RW 0x0007 +#define NVREG_MIISTAT_MASK_ALL 0x000f + + NvRegMIIMask = 0x184, +#define NVREG_MII_LINKCHANGE 0x0008 + + NvRegAdapterControl = 0x188, +#define NVREG_ADAPTCTL_START 0x02 +#define NVREG_ADAPTCTL_LINKUP 0x04 +#define NVREG_ADAPTCTL_PHYVALID 0x40000 +#define NVREG_ADAPTCTL_RUNNING 0x100000 +#define NVREG_ADAPTCTL_PHYSHIFT 24 + + NvRegMIISpeed = 0x18c, +#define NVREG_MIISPEED_BIT8 (1<<8) +#define NVREG_MIIDELAY 5 + + NvRegMIIControl = 0x190, +#define NVREG_MIICTL_INUSE 0x08000 +#define NVREG_MIICTL_WRITE 0x00400 +#define NVREG_MIICTL_ADDRSHIFT 5 + + NvRegMIIData = 0x194, + NvRegTxUnicast = 0x1a0, + NvRegTxMulticast = 0x1a4, + NvRegTxBroadcast = 0x1a8, + + NvRegWakeUpFlags = 0x200, +#define NVREG_WAKEUPFLAGS_VAL 0x7770 +#define NVREG_WAKEUPFLAGS_BUSYSHIFT 24 +#define NVREG_WAKEUPFLAGS_ENABLESHIFT 16 +#define NVREG_WAKEUPFLAGS_D3SHIFT 12 +#define NVREG_WAKEUPFLAGS_D2SHIFT 8 +#define NVREG_WAKEUPFLAGS_D1SHIFT 4 +#define NVREG_WAKEUPFLAGS_D0SHIFT 0 +#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01 +#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02 +#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04 +#define NVREG_WAKEUPFLAGS_ENABLE_MAGPAT 0x1111 +#define NVREG_WAKEUPFLAGS_ENABLE_WAKEUPPAT 0x2222 +#define NVREG_WAKEUPFLAGS_ENABLE_LINKCHANGE 0x4444 + + NvRegPatternCrc = 0x204, + NvRegPatternMask0 = 0x208, + NvRegPatternMask1 = 0x20C, + NvRegPatternMask2 = 0x210, + NvRegPatternMask3 = 0x214, + + NvRegMgmtUnitGetVersion = 0x204, +#define NVREG_MGMTUNITGETVERSION 0x01 + + NvRegMgmtUnitVersion = 0x208, +#define NVREG_MGMTUNITVERSION 0x08 + + NvRegPowerCap = 0x268, +#define NVREG_POWERCAP_D3SUPP (1<<30) +#define NVREG_POWERCAP_D2SUPP (1<<26) +#define NVREG_POWERCAP_D1SUPP (1<<25) + + NvRegPowerState = 0x26c, +#define NVREG_POWERSTATE_POWEREDUP 0x8000 +#define NVREG_POWERSTATE_VALID 0x0100 +#define NVREG_POWERSTATE_MASK 0x0003 +#define NVREG_POWERSTATE_D0 0x0000 +#define NVREG_POWERSTATE_D1 0x0001 +#define NVREG_POWERSTATE_D2 0x0002 +#define NVREG_POWERSTATE_D3 0x0003 + + NvRegMgmtUnitControl = 0x278, +#define NVREG_MGMTUNITCONTROL_INUSE 0x20000 + + NvRegTxCnt = 0x280, + NvRegTxZeroReXmt = 0x284, + NvRegTxOneReXmt = 0x288, + NvRegTxManyReXmt = 0x28c, + NvRegTxLateCol = 0x290, + NvRegTxUnderflow = 0x294, + NvRegTxLossCarrier = 0x298, + NvRegTxExcessDef = 0x29c, + NvRegTxRetryErr = 0x2a0, + NvRegRxFrameErr = 0x2a4, + NvRegRxExtraByte = 0x2a8, + NvRegRxLateCol = 0x2ac, + NvRegRxRunt = 0x2b0, + NvRegRxFrameTooLong = 0x2b4, + NvRegRxOverflow = 0x2b8, + NvRegRxFCSErr = 0x2bc, + NvRegRxFrameAlignErr = 0x2c0, + NvRegRxLenErr = 0x2c4, + NvRegRxUnicast = 0x2c8, + NvRegRxMulticast = 0x2cc, + NvRegRxBroadcast = 0x2d0, + NvRegTxDef = 0x2d4, + NvRegTxFrame = 0x2d8, + NvRegRxCnt = 0x2dc, + NvRegTxPause = 0x2e0, + NvRegRxPause = 0x2e4, + NvRegRxDropFrame = 0x2e8, + + NvRegVlanControl = 0x300, +#define NVREG_VLANCONTROL_ENABLE 0x2000 + + NvRegMSIXMap0 = 0x3e0, + NvRegMSIXMap1 = 0x3e4, + NvRegMSIXIrqStatus = 0x3f0, + + NvRegPowerState2 = 0x600, +#define NVREG_POWERSTATE2_POWERUP_MASK 0x0F15 +#define NVREG_POWERSTATE2_POWERUP_REV_A3 0x0001 +#define NVREG_POWERSTATE2_PHY_RESET 0x0004 +#define NVREG_POWERSTATE2_GATE_CLOCK_1 0x0100 +#define NVREG_POWERSTATE2_GATE_CLOCK_2 0x0200 +#define NVREG_POWERSTATE2_GATE_CLOCK_3 0x0400 +#define NVREG_POWERSTATE2_GATE_CLOCKS 0x0F00 +#define NVREG_POWERSTATE2_WAKEUPPAT_5 (1<<16) +#define NVREG_POWERSTATE2_WAKEUPPAT_6 (1<<17) +#define NVREG_POWERSTATE2_WAKEUPPAT_7 (1<<18) + + NvRegPatternCrcEx = 0x604, + NvRegPatternMask0Ex = 0x608, + NvRegPatternMask1Ex = 0x60C, + NvRegPatternMask2Ex = 0x610, + NvRegPatternMask3Ex = 0x614 +} NVNET_REGISTER; + +#include +typedef struct _NVNET_DESCRIPTOR_32 +{ + ULONG Address; + ULONG FlagsLength; +} NVNET_DESCRIPTOR_32, *PNVNET_DESCRIPTOR_32; + +typedef struct _NVNET_DESCRIPTOR_64 +{ + ULONG AddressHigh; + ULONG AddressLow; + ULONG VlanTag; + ULONG FlagsLength; +} NVNET_DESCRIPTOR_64, *PNVNET_DESCRIPTOR_64; +#include + +#define FLAG_MASK_V1 0xffff0000 +#define FLAG_MASK_V2 0xffffc000 +#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1) +#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2) + +#define NV_TX_LASTPACKET (1<<16) +#define NV_TX_RETRYERROR (1<<19) +#define NV_TX_RETRYCOUNT_MASK (0xF<<20) +#define NV_TX_ONE_RETRY (1<<20) +#define NV_TX_FORCED_INTERRUPT (1<<24) +#define NV_TX_DEFERRED (1<<26) +#define NV_TX_CARRIERLOST (1<<27) +#define NV_TX_LATECOLLISION (1<<28) +#define NV_TX_UNDERFLOW (1<<29) +#define NV_TX_ERROR (1<<30) +#define NV_TX_VALID (1<<31) + +#define NV_TX2_LASTPACKET (1<<29) +#define NV_TX2_RETRYERROR (1<<18) +#define NV_TX2_RETRYCOUNT_MASK (0xF<<19) +#define NV_TX2_FORCED_INTERRUPT (1<<30) +#define NV_TX2_DEFERRED (1<<25) +#define NV_TX2_CARRIERLOST (1<<26) +#define NV_TX2_LATECOLLISION (1<<27) +#define NV_TX2_UNDERFLOW (1<<28) +/* Error and valid are the same for both */ +#define NV_TX2_ERROR (1<<30) +#define NV_TX2_VALID (1<<31) +#define NV_TX2_TSO (1<<28) +#define NV_TX2_TSO_SHIFT 14 +#define NV_TX2_TSO_MAX_SHIFT 14 +#define NV_TX2_CHECKSUM_L3 (1<<27) +#define NV_TX2_CHECKSUM_L4 (1<<26) + +#define NV_MAXIMUM_SG_SIZE (1< + */ + +/* INCLUDES *******************************************************************/ + +#include "nvnet.h" + +#define NDEBUG +#include "debug.h" + +/* GLOBALS ********************************************************************/ + +CODE_SEG("INIT") +DRIVER_INITIALIZE DriverEntry; + +/* FUNCTIONS ******************************************************************/ + +CODE_SEG("PAGE") +VOID +NvNetFlushTransmitQueue( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_STATUS CompleteStatus) +{ + PNVNET_TCB Tcb; + + PAGED_CODE(); + + for (Tcb = Adapter->Send.LastTcb; + Tcb != Adapter->Send.CurrentTcb; + Tcb = NV_NEXT_TCB(Adapter, Tcb)) + { + NdisMSendComplete(Adapter->AdapterHandle, + Tcb->Packet, + CompleteStatus); + + NV_RELEASE_TCB(Adapter, Tcb); + } +} + +DECLSPEC_NOINLINE /* Called from pageable code */ +VOID +NvNetPauseProcessing( + _In_ PNVNET_ADAPTER Adapter) +{ + NvNetDisableInterrupts(Adapter); + + NdisAcquireSpinLock(&Adapter->Send.Lock); + Adapter->Flags &= ~NV_ACTIVE; + NdisReleaseSpinLock(&Adapter->Send.Lock); +} + +CODE_SEG("PAGE") +VOID +NvNetStopAdapter( + _In_ PNVNET_ADAPTER Adapter) +{ + BOOLEAN Dummy; + + PAGED_CODE(); + + NdisMCancelTimer(&Adapter->MediaDetectionTimer, &Dummy); + + NvNetDisableInterrupts(Adapter); + + KeFlushQueuedDpcs(); +} + +CODE_SEG("PAGE") +VOID +NvNetStartAdapter( + _In_ PNVNET_ADAPTER Adapter) +{ + PAGED_CODE(); + + NdisMSynchronizeWithInterrupt(&Adapter->Interrupt, + NvNetInitPhaseSynchronized, + Adapter); + + /* Setup the link timer right after the NIC is initialized */ + if (Adapter->Features & DEV_NEED_LINKTIMER) + { + NdisMSetPeriodicTimer(&Adapter->MediaDetectionTimer, + NVNET_MEDIA_DETECTION_INTERVAL); + } +} + +static +CODE_SEG("PAGE") +VOID +NTAPI +NvNetResetWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context) +{ + PNVNET_ADAPTER Adapter = Context; + + PAGED_CODE(); + + NvNetStopAdapter(Adapter); + + NvNetIdleTransmitter(Adapter, TRUE); + NvNetStopTransmitter(Adapter); + NvNetStopReceiver(Adapter); + NV_WRITE(Adapter, NvRegTxRxControl, + Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET); + NdisStallExecution(NV_TXRX_RESET_DELAY); + + NvNetFlushTransmitQueue(Adapter, NDIS_STATUS_FAILURE); + + NT_VERIFY(NvNetInitNIC(Adapter, FALSE) == NDIS_STATUS_SUCCESS); + + NvNetStartAdapter(Adapter); + + _InterlockedDecrement(&Adapter->ResetLock); + + NdisMResetComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS, TRUE); +} + +/* NDIS CALLBACKS *************************************************************/ + +static +BOOLEAN +NTAPI +MiniportCheckForHang( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + + NdisDprAcquireSpinLock(&Adapter->Send.Lock); + + if (Adapter->Flags & NV_ACTIVE && + Adapter->Send.TcbSlots < NVNET_TRANSMIT_BLOCKS) + { + if (++Adapter->Send.StuckCount > NVNET_TRANSMIT_HANG_THRESHOLD) + { + NDIS_DbgPrint(MAX_TRACE, ("Transmit timeout!\n")); + +#if defined(SARCH_XBOX) + /* Apply a HACK to make XQEMU happy... */ + NvNetDisableInterrupts(Adapter); + NvNetApplyInterruptMask(Adapter); +#endif + + Adapter->Send.StuckCount = 0; + } + } + + NdisDprReleaseSpinLock(&Adapter->Send.Lock); + + return FALSE; +} + +static +CODE_SEG("PAGE") +VOID +NTAPI +MiniportHalt( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + BOOLEAN IsActive = !!(Adapter->Flags & NV_ACTIVE); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + PAGED_CODE(); + + if (IsActive) + { + NvNetPauseProcessing(Adapter); + } + + NvNetStopAdapter(Adapter); + + if (IsActive) + { + NvNetIdleTransmitter(Adapter, TRUE); + NvNetStopTransmitter(Adapter); + NvNetStopReceiver(Adapter); + NV_WRITE(Adapter, NvRegTxRxControl, + Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET); + NdisStallExecution(NV_TXRX_RESET_DELAY); + } + + NvNetFlushTransmitQueue(Adapter, NDIS_STATUS_FAILURE); + + NvNetToggleClockPowerGating(Adapter, TRUE); + + NV_WRITE(Adapter, NvRegMacAddrA, Adapter->OriginalMacAddress[0]); + NV_WRITE(Adapter, NvRegMacAddrB, Adapter->OriginalMacAddress[1]); + NV_WRITE(Adapter, NvRegTransmitPoll, + NV_READ(Adapter, NvRegTransmitPoll) & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV); + + SidebandUnitReleaseSemaphore(Adapter); + + NvNetFreeAdapter(Adapter); +} + +static +NDIS_STATUS +NTAPI +MiniportReset( + _Out_ PBOOLEAN AddressingReset, + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if (_InterlockedCompareExchange(&Adapter->ResetLock, 1, 0)) + { + return NDIS_STATUS_RESET_IN_PROGRESS; + } + + NvNetPauseProcessing(Adapter); + + NdisInitializeWorkItem(&Adapter->ResetWorkItem, NvNetResetWorker, Adapter); + NdisScheduleWorkItem(&Adapter->ResetWorkItem); + + return NDIS_STATUS_PENDING; +} + +static +VOID +NTAPI +MiniportShutdown( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if (Adapter->Flags & NV_ACTIVE) + { + if (KeGetCurrentIrql() <= DISPATCH_LEVEL) + { + NvNetPauseProcessing(Adapter); + } + + NvNetIdleTransmitter(Adapter, TRUE); + NvNetStopTransmitter(Adapter); + NvNetStopReceiver(Adapter); + NV_WRITE(Adapter, NvRegTxRxControl, + Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET); + NdisStallExecution(NV_TXRX_RESET_DELAY); + + SidebandUnitReleaseSemaphore(Adapter); + } + + NvNetDisableInterrupts(Adapter); + + NvNetSetPowerState(Adapter, NdisDeviceStateD3, 0); +} + +CODE_SEG("INIT") +NTSTATUS +NTAPI +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath) +{ + NDIS_HANDLE WrapperHandle; + NDIS_MINIPORT_CHARACTERISTICS Characteristics = { 0 }; + NDIS_STATUS Status; + + NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, NULL); + if (!WrapperHandle) + { + return NDIS_STATUS_FAILURE; + } + + Characteristics.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION; + Characteristics.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION; + Characteristics.CheckForHangHandler = MiniportCheckForHang; + Characteristics.HaltHandler = MiniportHalt; + Characteristics.HandleInterruptHandler = MiniportHandleInterrupt; + Characteristics.InitializeHandler = MiniportInitialize; + Characteristics.ISRHandler = MiniportISR; + Characteristics.QueryInformationHandler = MiniportQueryInformation; + Characteristics.ResetHandler = MiniportReset; + Characteristics.SendHandler = MiniportSend; /* TODO */ + Characteristics.SetInformationHandler = MiniportSetInformation; + // Characteristics.ReturnPacketHandler = MiniportReturnPacket; /* TODO */ + // Characteristics.SendPacketsHandler = MiniportSendPackets; /* TODO */ + Characteristics.AdapterShutdownHandler = MiniportShutdown; + + Status = NdisMRegisterMiniport(WrapperHandle, &Characteristics, sizeof(Characteristics)); + if (Status != NDIS_STATUS_SUCCESS) + { + NdisTerminateWrapper(WrapperHandle, NULL); + return Status; + } + + return Status; +} diff --git a/drivers/network/dd/nvnet/nvnet.h b/drivers/network/dd/nvnet/nvnet.h new file mode 100644 index 00000000000..d4bef9c4d9c --- /dev/null +++ b/drivers/network/dd/nvnet/nvnet.h @@ -0,0 +1,672 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Common header file + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +#ifndef _NVNET_PCH_ +#define _NVNET_PCH_ + +#if !DBG +#define NO_KERNEL_LIST_ENTRY_CHECKS +#endif +#include + +#include + +#include "eth.h" +#include "nic.h" +#include "phyreg.h" + +#define NVNET_TAG 'ENVN' + +#if defined(SARCH_XBOX) +/* Reduce memory requirements on OG Xbox */ +#define NVNET_TRANSMIT_BLOCKS 8 +#define NVNET_TRANSMIT_DESCRIPTORS 32 +#define NVNET_TRANSMIT_BUFFERS 1 +#define NVNET_RECEIVE_DESCRIPTORS 16 +#else +#define NVNET_TRANSMIT_BLOCKS 64 +#define NVNET_TRANSMIT_DESCRIPTORS 512 +#define NVNET_TRANSMIT_BUFFERS 16 +#define NVNET_RECEIVE_DESCRIPTORS 512 +#endif + +#define NVNET_ALIGNMENT 64 + +#define NVNET_RECEIVE_BUFFER_SIZE 2048 + +#define NVNET_RECEIVE_PROCESSING_LIMIT 64 + +#define NVNET_IM_THRESHOLD 4 +#define NVNET_IM_MAX_IDLE 40 + +#if defined(SARCH_XBOX) +#define NVNET_TRANSMIT_HANG_THRESHOLD 3 +#else +#define NVNET_TRANSMIT_HANG_THRESHOLD 5 +#endif + +#if defined(SARCH_XBOX) +#define NVNET_FRAGMENTATION_THRESHOLD 8 +#else +#define NVNET_FRAGMENTATION_THRESHOLD 32 +#endif + +#define NVNET_MEDIA_DETECTION_INTERVAL 5000 + +#define NVNET_MAXIMUM_FRAME_SIZE 1514 +#define NVNET_MAXIMUM_FRAME_SIZE_JUMBO 9014 +#define NVNET_MAXIMUM_VLAN_ID 0xFFF + +#define NVNET_MULTICAST_LIST_SIZE 32 + +#define NVNET_MINIMUM_LSO_SEGMENT_COUNT 2 +#define NVNET_MAXIMUM_LSO_FRAME_SIZE 0xFC00 + +#define NVNET_PACKET_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) + +#define PACKET_ENTRY(Packet) ((PLIST_ENTRY)(&(Packet)->MiniportReserved[0])) + +typedef enum _NVNET_OPTIMIZATION_MODE +{ + NV_OPTIMIZATION_MODE_DYNAMIC = 0, + NV_OPTIMIZATION_MODE_CPU, + NV_OPTIMIZATION_MODE_THROUGHPUT +} NVNET_OPTIMIZATION_MODE; + +typedef enum _NVNET_FLOW_CONTROL_MODE +{ + NV_FLOW_CONTROL_DISABLE = 0, + NV_FLOW_CONTROL_AUTO, + NV_FLOW_CONTROL_RX, + NV_FLOW_CONTROL_TX, + NV_FLOW_CONTROL_RX_TX +} NVNET_FLOW_CONTROL_MODE; + +typedef union _NVNET_OFFLOAD +{ + struct { + ULONG SendIpOptions:1; + ULONG SendTcpOptions:1; + ULONG SendTcpChecksum:1; + ULONG SendUdpChecksum:1; + ULONG SendIpChecksum:1; + + ULONG ReceiveIpOptions:1; + ULONG ReceiveTcpOptions:1; + ULONG ReceiveTcpChecksum:1; + ULONG ReceiveUdpChecksum:1; + ULONG ReceiveIpChecksum:1; + + ULONG SendIpV6Options:1; + ULONG SendTcpV6Options:1; + ULONG SendTcpV6Checksum:1; + ULONG SendUdpV6Checksum:1; + + ULONG ReceiveIpV6Options:1; + ULONG ReceiveTcpV6Options:1; + ULONG ReceiveTcpV6Checksum:1; + ULONG ReceiveUdpV6Checksum:1; + }; + ULONG Value; +} NVNET_OFFLOAD, *PNVNET_OFFLOAD; + +typedef struct _NVNET_STATISTICS +{ + ULONG64 HwTxCnt; + ULONG64 HwTxZeroReXmt; + ULONG64 HwTxOneReXmt; + ULONG64 HwTxManyReXmt; + ULONG64 HwTxLateCol; + ULONG64 HwTxUnderflow; + ULONG64 HwTxLossCarrier; + ULONG64 HwTxExcessDef; + ULONG64 HwTxRetryErr; + ULONG64 HwRxFrameErr; + ULONG64 HwRxExtraByte; + ULONG64 HwRxLateCol; + ULONG64 HwRxRunt; + ULONG64 HwRxFrameTooLong; + ULONG64 HwRxOverflow; + ULONG64 HwRxFCSErr; + ULONG64 HwRxFrameAlignErr; + ULONG64 HwRxLenErr; + ULONG64 HwTxDef; + ULONG64 HwTxFrame; + ULONG64 HwRxCnt; + ULONG64 HwTxPause; + ULONG64 HwRxPause; + ULONG64 HwRxDropFrame; + ULONG64 HwRxUnicast; + ULONG64 HwRxMulticast; + ULONG64 HwRxBroadcast; + ULONG64 HwTxUnicast; + ULONG64 HwTxMulticast; + ULONG64 HwTxBroadcast; + + ULONG64 TransmitOk; + ULONG64 ReceiveOk; + ULONG64 TransmitErrors; + ULONG64 ReceiveErrors; + ULONG64 ReceiveNoBuffers; + ULONG64 ReceiveCrcErrors; + ULONG64 ReceiveAlignmentErrors; + ULONG64 TransmitDeferred; + ULONG64 TransmitExcessiveCollisions; + ULONG64 ReceiveOverrunErrors; + ULONG64 TransmitUnderrunErrors; + ULONG64 TransmitZeroRetry; + ULONG64 TransmitOneRetry; + ULONG64 TransmitLostCarrierSense; + ULONG64 TransmitLateCollisions; + + ULONG ReceiveIrqNoBuffers; +} NVNET_STATISTICS, *PNVNET_STATISTICS; + +typedef struct _NVNET_WAKE_FRAME +{ + union + { + UCHAR AsUCHAR[16]; + ULONG AsULONG[4]; + } PatternMask; + UCHAR WakeUpPattern[128]; +} NVNET_WAKE_FRAME, *PNVNET_WAKE_FRAME; + +typedef struct _NVNET_TX_BUFFER_DATA +{ + PVOID VirtualAddress; + NDIS_PHYSICAL_ADDRESS PhysicalAddress; +} NVNET_TX_BUFFER_DATA, *PNVNET_TX_BUFFER_DATA; + +typedef struct _NVNET_TX_BUFFER +{ + SINGLE_LIST_ENTRY Link; + PVOID VirtualAddress; + NDIS_PHYSICAL_ADDRESS PhysicalAddress; +} NVNET_TX_BUFFER, *PNVNET_TX_BUFFER; + +typedef union _NVNET_TBD +{ + PNVNET_DESCRIPTOR_32 x32; + PNVNET_DESCRIPTOR_64 x64; + PVOID Memory; +} NVNET_TBD; + +typedef struct _NVNET_TCB +{ + NVNET_TBD Tbd; + NVNET_TBD DeferredTbd; + PNDIS_PACKET Packet; + PNVNET_TX_BUFFER Buffer; + ULONG Slots; + ULONG Flags; +#define NV_TCB_LARGE_SEND 0x00000001 +#define NV_TCB_CHECKSUM_IP 0x00000002 +#define NV_TCB_CHECKSUM_TCP 0x00000004 +#define NV_TCB_CHECKSUM_UDP 0x00000008 +#define NV_TCB_COALESCE 0x00000010 + + ULONG Mss; +} NVNET_TCB, *PNVNET_TCB; + +typedef union _NV_RBD +{ + PNVNET_DESCRIPTOR_32 x32; + PNVNET_DESCRIPTOR_64 x64; + PVOID Memory; +} NV_RBD; + +typedef struct _NVNET_RBD +{ + NV_RBD NvRbd; + PNDIS_PACKET Packet; + PNDIS_BUFFER Buffer; +} NVNET_RBD, *PNVNET_RBD; + +typedef struct _NVNET_SEND +{ + NDIS_SPIN_LOCK Lock; + PNVNET_TCB HeadTcb; + PNVNET_TCB TailTcb; + PNVNET_TCB LastTcb; + PNVNET_TCB CurrentTcb; + PNVNET_TCB DeferredTcb; + NVNET_TBD HeadTbd; + NVNET_TBD TailTbd; + NVNET_TBD CurrentTbd; + ULONG TcbSlots; + ULONG TbdSlots; + ULONG StuckCount; + ULONG PacketsCount; + SINGLE_LIST_ENTRY BufferList; +} NVNET_SEND, *PNVNET_SEND; + +typedef struct _NVNET_RECEIVE +{ + NDIS_SPIN_LOCK Lock; + NV_RBD NvRbd; +} NVNET_RECEIVE, *PNVNET_RECEIVE; + +typedef struct _NVNET_ADAPTER NVNET_ADAPTER, *PNVNET_ADAPTER; + +typedef VOID +(NVNET_TRANSMIT_PACKET)( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNVNET_TCB Tcb, + _In_ PSCATTER_GATHER_LIST SgList); +typedef NVNET_TRANSMIT_PACKET *PNVNET_TRANSMIT_PACKET; + +typedef ULONG +(NVNET_PROCESS_TRANSMIT)( + _In_ PNVNET_ADAPTER Adapter, + _Inout_ PLIST_ENTRY SendReadyList); +typedef NVNET_PROCESS_TRANSMIT *PNVNET_PROCESS_TRANSMIT; + +typedef ULONG +(NVNET_PROCESS_RECEIVE)( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG TotalRxProcessed); +typedef NVNET_PROCESS_RECEIVE *PNVNET_PROCESS_RECEIVE; + +typedef struct _NVNET_ADAPTER +{ + volatile PUCHAR IoBase; + NDIS_HANDLE AdapterHandle; + ULONG Features; + ULONG Flags; +#define NV_ACTIVE 0x80000000 +#define NV_SEND_CHECKSUM 0x00000002 +#define NV_SEND_LARGE_SEND 0x00000004 +#define NV_SEND_ERRATA_PRESENT 0x00000008 + +#define NV_MAC_IN_USE 0x00000010 +#define NV_GIGABIT_PHY 0x00000020 +#define NV_UNIT_SEMAPHORE_ACQUIRED 0x00000040 +#define NV_USE_SOFT_MAC_ADDRESS 0x00000100 +#define NV_FORCE_SPEED_AND_DUPLEX 0x00000200 +#define NV_FORCE_FULL_DUPLEX 0x00000400 +#define NV_USER_SPEED_100 0x00000800 +#define NV_PACKET_PRIORITY 0x00001000 +#define NV_VLAN_TAGGING 0x00002000 + + ULONG TxRxControl; + ULONG InterruptMask; + ULONG InterruptStatus; + ULONG InterruptIdleCount; + ULONG AdapterStatus; + NVNET_OPTIMIZATION_MODE OptimizationMode; + NVNET_OFFLOAD Offload; + ULONG IpHeaderOffset; + ULONG PacketFilter; + NVNET_SEND Send; + + NVNET_RECEIVE Receive; + PUCHAR ReceiveBuffer; + ULONG CurrentRx; + + PNVNET_TRANSMIT_PACKET TransmitPacket; + PNVNET_PROCESS_TRANSMIT ProcessTransmit; + + NVNET_STATISTICS Statistics; + NDIS_SPIN_LOCK Lock; + ULONG MaximumFrameSize; + ULONG ReceiveBufferSize; + ULONG VlanId; + ULONG WakeFlags; + ULONG PhyAddress; + ULONG PhyModel; + ULONG PhyRevision; + ULONG PhyOui; + ULONG PowerStatePending; + + ULONG VlanControl; + ULONG PauseFlags; + ULONG LinkSpeed; + BOOLEAN Connected; + BOOLEAN FullDuplex; + + ULONG OriginalMacAddress[2]; + UCHAR PermanentMacAddress[ETH_LENGTH_OF_ADDRESS]; + UCHAR CurrentMacAddress[ETH_LENGTH_OF_ADDRESS]; + + _Field_range_(0, NVNET_MULTICAST_LIST_SIZE) + ULONG MulticastListSize; + struct + { + UCHAR MacAddress[ETH_LENGTH_OF_ADDRESS]; + } MulticastList[NVNET_MULTICAST_LIST_SIZE]; + + ULONG WakeFrameBitmap; + PNVNET_WAKE_FRAME WakeFrames[NV_WAKEUPPATTERNS_V2]; + + NDIS_HANDLE WrapperConfigurationHandle; + + NDIS_WORK_ITEM PowerWorkItem; + NDIS_WORK_ITEM ResetWorkItem; + + _Interlocked_ + volatile LONG ResetLock; + + NDIS_PHYSICAL_ADDRESS IoAddress; + ULONG IoLength; + + NVNET_FLOW_CONTROL_MODE FlowControlMode; + NDIS_MINIPORT_TIMER MediaDetectionTimer; + + USHORT DeviceId; + UCHAR RevisionId; + + BOOLEAN InterruptShared; + NDIS_MINIPORT_INTERRUPT Interrupt; + ULONG InterruptVector; + ULONG InterruptLevel; + ULONG InterruptFlags; + + NDIS_PHYSICAL_ADDRESS TbdPhys; + NDIS_PHYSICAL_ADDRESS RbdPhys; + NDIS_PHYSICAL_ADDRESS ReceiveBufferPhys; + + PVOID SendBuffer; + PVOID TbdOriginal; + PVOID RbdOriginal; + PVOID AdapterOriginal; + NDIS_PHYSICAL_ADDRESS TbdPhysOriginal; + NDIS_PHYSICAL_ADDRESS RbdPhysOriginal; + NVNET_TX_BUFFER_DATA SendBufferAllocationData[NVNET_TRANSMIT_BUFFERS]; +} NVNET_ADAPTER, *PNVNET_ADAPTER; + +#define NvNetLogError(Adapter, ErrorCode) \ + NdisWriteErrorLogEntry((Adapter)->AdapterHandle, ErrorCode, 1, __LINE__) + +NVNET_TRANSMIT_PACKET NvNetTransmitPacket32; +NVNET_TRANSMIT_PACKET NvNetTransmitPacket64; +NVNET_PROCESS_TRANSMIT ProcessTransmitDescriptorsLegacy; +NVNET_PROCESS_TRANSMIT ProcessTransmitDescriptors32; +NVNET_PROCESS_TRANSMIT ProcessTransmitDescriptors64; + +CODE_SEG("PAGE") +NDIS_STATUS +NTAPI +MiniportInitialize( + _Out_ PNDIS_STATUS OpenErrorStatus, + _Out_ PUINT SelectedMediumIndex, + _In_ PNDIS_MEDIUM MediumArray, + _In_ UINT MediumArraySize, + _In_ NDIS_HANDLE MiniportAdapterHandle, + _In_ NDIS_HANDLE WrapperConfigurationContext); + +CODE_SEG("PAGE") +VOID +NvNetFreeAdapter( + _In_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +NDIS_STATUS +NvNetRecognizeHardware( + _Inout_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +NDIS_STATUS +NvNetGetPermanentMacAddress( + _Inout_ PNVNET_ADAPTER Adapter, + _Out_writes_bytes_all_(ETH_LENGTH_OF_ADDRESS) PUCHAR MacAddress); + +CODE_SEG("PAGE") +VOID +NvNetSetupMacAddress( + _In_ PNVNET_ADAPTER Adapter, + _In_reads_bytes_(ETH_LENGTH_OF_ADDRESS) PUCHAR MacAddress); + +CODE_SEG("PAGE") +NDIS_STATUS +NvNetInitNIC( + _In_ PNVNET_ADAPTER Adapter, + _In_ BOOLEAN InitPhy); + +CODE_SEG("PAGE") +NDIS_STATUS +NvNetFindPhyDevice( + _In_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +NDIS_STATUS +NvNetPhyInit( + _In_ PNVNET_ADAPTER Adapter); + +VOID +SidebandUnitReleaseSemaphore( + _In_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +NvNetStartAdapter( + _In_ PNVNET_ADAPTER Adapter); + +DECLSPEC_NOINLINE +VOID +NvNetPauseProcessing( + _In_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +NvNetStopAdapter( + _In_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +NvNetFlushTransmitQueue( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_STATUS CompleteStatus); + +KSYNCHRONIZE_ROUTINE NvNetInitPhaseSynchronized; +NDIS_TIMER_FUNCTION NvNetMediaDetectionDpc; + +BOOLEAN +MiiWrite( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _In_ ULONG Data); + +BOOLEAN +MiiRead( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _Out_ PULONG Data); + +BOOLEAN +NvNetUpdateLinkSpeed( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetResetReceiverAndTransmitter( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetStartReceiver( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetStartTransmitter( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetStopReceiver( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetStopTransmitter( + _In_ PNVNET_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +NvNetIdleTransmitter( + _In_ PNVNET_ADAPTER Adapter, + _In_ BOOLEAN ClearPhyControl); + +VOID +NvNetUpdatePauseFrame( + _Inout_ PNVNET_ADAPTER Adapter, + _In_ ULONG PauseFlags); + +VOID +NvNetToggleClockPowerGating( + _In_ PNVNET_ADAPTER Adapter, + _In_ BOOLEAN Gate); + +VOID +NvNetSetPowerState( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE NewPowerState, + _In_ ULONG WakeFlags); + +CODE_SEG("PAGE") +VOID +NvNetBackoffSetSlotTime( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetBackoffReseed( + _In_ PNVNET_ADAPTER Adapter); + +VOID +NvNetBackoffReseedEx( + _In_ PNVNET_ADAPTER Adapter); + +NDIS_STATUS +NTAPI +MiniportSend( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_PACKET Packet, + _In_ UINT Flags); + +VOID +NTAPI +MiniportISR( + _Out_ PBOOLEAN InterruptRecognized, + _Out_ PBOOLEAN QueueMiniportHandleInterrupt, + _In_ NDIS_HANDLE MiniportAdapterContext); + +VOID +NTAPI +MiniportHandleInterrupt( + _In_ NDIS_HANDLE MiniportAdapterContext); + +NDIS_STATUS +NTAPI +MiniportQueryInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesWritten, + _Out_ PULONG BytesNeeded); + +NDIS_STATUS +NTAPI +MiniportSetInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesRead, + _Out_ PULONG BytesNeeded); + +#define NV_IMPLICIT_ENTRIES(Length) \ + (((Length - (NV_MAXIMUM_SG_SIZE + 1)) >> NV_TX2_TSO_MAX_SHIFT) + 1) + +FORCEINLINE +VOID +NV_RELEASE_TCB( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNVNET_TCB Tcb) +{ + if (Tcb->Flags & NV_TCB_COALESCE) + { + PushEntryList(&Adapter->Send.BufferList, &Tcb->Buffer->Link); + } + + Tcb->Packet = NULL; + + ++Adapter->Send.TcbSlots; + + Adapter->Send.TbdSlots += Tcb->Slots; + + Adapter->Send.StuckCount = 0; +} + +FORCEINLINE +PNVNET_TCB +NV_NEXT_TCB( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNVNET_TCB Tcb) +{ + if (Tcb++ == Adapter->Send.TailTcb) + return Adapter->Send.HeadTcb; + else + return Tcb; +} + +FORCEINLINE +NVNET_TBD +NV_NEXT_TBD_32( + _In_ PNVNET_ADAPTER Adapter, + _In_ NVNET_TBD Tbd) +{ + if (Tbd.x32++ == Adapter->Send.TailTbd.x32) + return Adapter->Send.HeadTbd; + else + return Tbd; +} + +FORCEINLINE +NVNET_TBD +NV_NEXT_TBD_64( + _In_ PNVNET_ADAPTER Adapter, + _In_ NVNET_TBD Tbd) +{ + if (Tbd.x64++ == Adapter->Send.TailTbd.x64) + return Adapter->Send.HeadTbd; + else + return Tbd; +} + +FORCEINLINE +VOID +NV_WRITE( + _In_ PNVNET_ADAPTER Adapter, + _In_ NVNET_REGISTER Register, + _In_ ULONG Value) +{ + NdisWriteRegisterUlong((PULONG)(Adapter->IoBase + Register), Value); +} + +FORCEINLINE +ULONG +NV_READ( + _In_ PNVNET_ADAPTER Adapter, + _In_ NVNET_REGISTER Register) +{ + ULONG Value; + + NdisReadRegisterUlong((PULONG)(Adapter->IoBase + Register), &Value); + return Value; +} + +#define NvNetDisableInterrupts(Adapter) \ + NV_WRITE(Adapter, NvRegIrqMask, 0); + +#define NvNetApplyInterruptMask(Adapter) \ + NV_WRITE(Adapter, NvRegIrqMask, (Adapter)->InterruptMask); + +#endif /* _NVNET_PCH_ */ diff --git a/drivers/network/dd/nvnet/nvnet.rc b/drivers/network/dd/nvnet/nvnet.rc new file mode 100644 index 00000000000..edaa46d858d --- /dev/null +++ b/drivers/network/dd/nvnet/nvnet.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "nVidia nForce Ethernet Controller Driver" +#define REACTOS_STR_INTERNAL_NAME "nvnet" +#define REACTOS_STR_ORIGINAL_FILENAME "nvnet.sys" +#include diff --git a/drivers/network/dd/nvnet/phy.c b/drivers/network/dd/nvnet/phy.c new file mode 100644 index 00000000000..3558a785161 --- /dev/null +++ b/drivers/network/dd/nvnet/phy.c @@ -0,0 +1,1144 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: PHY layer setup and management + * 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" + +/* GLOBALS ********************************************************************/ + +BOOLEAN +MiiWrite( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _In_ ULONG Data) +{ + ULONG i; + + NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_RW); + + if (NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE) + { + NV_WRITE(Adapter, NvRegMIIControl, NVREG_MIICTL_INUSE); + NdisStallExecution(NV_MIIBUSY_DELAY); + } + + NV_WRITE(Adapter, NvRegMIIData, Data); + NV_WRITE(Adapter, NvRegMIIControl, + NVREG_MIICTL_WRITE | (PhyAddress << NVREG_MIICTL_ADDRSHIFT) | RegAddress); + + for (i = NV_MIIPHY_DELAYMAX; i > 0; --i) + { + NdisStallExecution(NV_MIIPHY_DELAY); + + if (!(NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE)) + break; + } + if (i == 0) + { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +MiiRead( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _Out_ PULONG Data) +{ + ULONG i; + + NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_RW); + + if (NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE) + { + NV_WRITE(Adapter, NvRegMIIControl, NVREG_MIICTL_INUSE); + NdisStallExecution(NV_MIIBUSY_DELAY); + } + + NV_WRITE(Adapter, NvRegMIIControl, (PhyAddress << NVREG_MIICTL_ADDRSHIFT) | RegAddress); + + for (i = NV_MIIPHY_DELAYMAX; i > 0; --i) + { + NdisStallExecution(NV_MIIPHY_DELAY); + + if (!(NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE)) + break; + } + if (i == 0) + { + *Data = 0; + return FALSE; + } + + if (NV_READ(Adapter, NvRegMIIStatus) & NVREG_MIISTAT_ERROR) + { + *Data = 0; + return FALSE; + } + + *Data = NV_READ(Adapter, NvRegMIIData); + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PhyInitRealtek8211b( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG i; + const struct + { + ULONG Register; + ULONG Data; + } Sequence[] = + { + { PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 }, + { PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 }, + { PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 }, + { PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 }, + { PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 }, + { PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 }, + { PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 } + }; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + for (i = 0; i < RTL_NUMBER_OF(Sequence); ++i) + { + if (!MiiWrite(Adapter, Adapter->PhyAddress, Sequence[i].Register, Sequence[i].Data)) + return FALSE; + } + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PhyInitRealtek8211c( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG PowerState, MiiRegister; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + PowerState = NV_READ(Adapter, NvRegPowerState2); + + NV_WRITE(Adapter, NvRegPowerState2, PowerState | NVREG_POWERSTATE2_PHY_RESET); + NdisMSleep(25000); + + NV_WRITE(Adapter, NvRegPowerState2, PowerState); + NdisMSleep(25000); + + MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, &MiiRegister); + MiiRegister |= PHY_REALTEK_INIT9; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, MiiRegister)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG7, &MiiRegister); + if (!(MiiRegister & PHY_REALTEK_INIT11)) + { + MiiRegister |= PHY_REALTEK_INIT11; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG7, MiiRegister)) + return FALSE; + } + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) + return FALSE; + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PhyInitRealtek8201( + _In_ PNVNET_ADAPTER Adapter, + _In_ BOOLEAN DisableCrossoverDetection) +{ + ULONG MiiRegister; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if (Adapter->Features & DEV_NEED_PHY_INIT_FIX) + { + MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, &MiiRegister); + MiiRegister |= PHY_REALTEK_INIT7; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, MiiRegister)) + return FALSE; + } + + if (DisableCrossoverDetection) + { + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG2, &MiiRegister); + MiiRegister &= ~PHY_REALTEK_INIT_MSK1; + MiiRegister |= PHY_REALTEK_INIT3; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG2, MiiRegister)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) + return FALSE; + } + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PhyInitCicadaSemiconductor( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG PhyInterface) +{ + ULONG MiiRegister; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if (PhyInterface & PHY_RGMII) + { + MiiRead(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG2, &MiiRegister); + MiiRegister &= ~(PHY_CICADA_INIT1 | PHY_CICADA_INIT2); + MiiRegister |= (PHY_CICADA_INIT3 | PHY_CICADA_INIT4); + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG2, MiiRegister)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG3, &MiiRegister); + MiiRegister |= PHY_CICADA_INIT5; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG3, MiiRegister)) + return FALSE; + } + + MiiRead(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG1, &MiiRegister); + MiiRegister |= PHY_CICADA_INIT6; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG1, MiiRegister)) + return FALSE; + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PhyInitVitesseSemiconductor( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG MiiRegister; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT1)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT2)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, &MiiRegister); + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, MiiRegister)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, &MiiRegister); + MiiRegister &= ~PHY_VITESSE_INIT_MSK1; + MiiRegister |= PHY_VITESSE_INIT3; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, MiiRegister)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT4)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT5)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, &MiiRegister); + MiiRegister &= ~PHY_VITESSE_INIT_MSK1; + MiiRegister |= PHY_VITESSE_INIT3; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, MiiRegister)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, &MiiRegister); + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, MiiRegister)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT6)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT7)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, &MiiRegister); + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, MiiRegister)) + return FALSE; + + MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, &MiiRegister); + MiiRegister &= ~PHY_VITESSE_INIT_MSK2; + MiiRegister |= PHY_VITESSE_INIT8; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, MiiRegister)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT9)) + return FALSE; + + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT10)) + return FALSE; + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +PhyReset( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG ControlSetup) +{ + ULONG Tries = 0, MiiControl = MII_CR_RESET | ControlSetup; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl)) + return FALSE; + + NdisMSleep(500000); + + do + { + NdisMSleep(10000); + + MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); + + if (Tries++ > 100) + return FALSE; + } + while (MiiControl & MII_CR_RESET); + + return TRUE; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +PhyInit( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG PhyInterface, MiiRegister, MiiStatus, MiiControl; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + /* PHY errata for E3016 PHY */ + if (Adapter->PhyModel == PHY_MODEL_MARVELL_E3016) + { + MiiRead(Adapter, Adapter->PhyAddress, PHY_MARVELL_INIT_REG1, &MiiRegister); + MiiRegister &= ~PHY_MARVELL_E3016_INITMASK; + if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_MARVELL_INIT_REG1, MiiRegister)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + + if (Adapter->PhyOui == PHY_OUI_REALTEK) + { + if (Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && + Adapter->PhyRevision == PHY_REV_REALTEK_8211B) + { + if (!PhyInitRealtek8211b(Adapter)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else if (Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && + Adapter->PhyRevision == PHY_REV_REALTEK_8211C) + { + if (!PhyInitRealtek8211c(Adapter)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else if (Adapter->PhyModel == PHY_MODEL_REALTEK_8201) + { + if (!PhyInitRealtek8201(Adapter, FALSE)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + } + + /* Set advertise register */ + MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, &MiiRegister); + if (Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX) + { + MiiRegister &= ~(MII_ADV_10T_HD | MII_ADV_10T_FD | MII_ADV_100T_HD | MII_ADV_100T_FD | + MII_ADV_100T4 | MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM); + + if (Adapter->Flags & NV_USER_SPEED_100) + { + if (Adapter->Flags & NV_FORCE_FULL_DUPLEX) + MiiRegister |= MII_ADV_100T_FD; + else + MiiRegister |= MII_ADV_100T_HD; + } + else + { + if (Adapter->Flags & NV_FORCE_FULL_DUPLEX) + MiiRegister |= MII_ADV_10T_FD; + else + MiiRegister |= MII_ADV_10T_HD; + } + + Adapter->PauseFlags &= ~(NV_PAUSEFRAME_AUTONEG | NV_PAUSEFRAME_RX_ENABLE | + NV_PAUSEFRAME_TX_ENABLE); + if (Adapter->PauseFlags & NV_PAUSEFRAME_RX_REQ) + { + /* For RX we set both advertisements but disable TX pause */ + MiiRegister |= MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM; + Adapter->PauseFlags |= NV_PAUSEFRAME_RX_ENABLE; + } + if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_REQ) + { + MiiRegister |= MII_ADV_PAUSE_ASYM; + Adapter->PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; + } + } + else + { + MiiRegister |= MII_ADV_10T_HD | MII_ADV_10T_FD | MII_ADV_100T_HD | + MII_ADV_100T_FD | MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM; + } + if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiRegister)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed!\n")); + return NDIS_STATUS_FAILURE; + } + + /* Get PHY interface type */ + PhyInterface = NV_READ(Adapter, NvRegPhyInterface); + + /* See if gigabit PHY */ + MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); + if (MiiStatus & PHY_GIGABIT) + { + ULONG MiiControl1000; + + Adapter->Flags |= NV_GIGABIT_PHY; + + MiiRead(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_CONTROL, &MiiControl1000); + MiiControl1000 &= ~MII_MS_CR_1000T_HD; + if ((PhyInterface & PHY_RGMII) && !(Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX)) + MiiControl1000 |= MII_MS_CR_1000T_FD; + else + MiiControl1000 &= ~MII_MS_CR_1000T_FD; + if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_CONTROL, MiiControl1000)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else + { + Adapter->Flags &= ~NV_GIGABIT_PHY; + } + + MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); + MiiControl |= MII_CR_AUTONEG; + if (Adapter->PhyOui == PHY_OUI_REALTEK && + Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && + Adapter->PhyRevision == PHY_REV_REALTEK_8211C) + { + /* Start auto-negation since we already performed HW reset above */ + MiiControl |= MII_CR_AUTONEG_RESTART; + if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else + { + /* Reset the PHY (certain PHYs need BMCR to be setup with reset) */ + if (!PhyReset(Adapter, MiiControl)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY reset failed\n")); + return NDIS_STATUS_FAILURE; + } + } + + /* PHY vendor specific configuration */ + if (Adapter->PhyOui == PHY_OUI_CICADA) + { + if (!PhyInitCicadaSemiconductor(Adapter, PhyInterface)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else if (Adapter->PhyOui == PHY_OUI_VITESSE) + { + if (!PhyInitVitesseSemiconductor(Adapter)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else if (Adapter->PhyOui == PHY_OUI_REALTEK) + { + if (Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && + Adapter->PhyRevision == PHY_REV_REALTEK_8211B) + { + /* Reset could have cleared these out, set them back */ + if (!PhyInitRealtek8211b(Adapter)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + else if (Adapter->PhyModel == PHY_MODEL_REALTEK_8201) + { + if (!PhyInitRealtek8201(Adapter, TRUE)) + { + NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); + return NDIS_STATUS_FAILURE; + } + } + } + + /* Some PHYs clear out pause advertisement on reset, set it back */ + MiiWrite(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiRegister); + + /* Restart auto-negotiation */ + MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); + MiiControl |= (MII_CR_AUTONEG_RESTART | MII_CR_AUTONEG); + if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl)) + return NDIS_STATUS_FAILURE; + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +BOOLEAN +FindPhyDevice( + _Inout_ PNVNET_ADAPTER Adapter) +{ + ULONG Phy; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + for (Phy = 1; Phy <= 32; ++Phy) + { + ULONG PhyAddress = Phy & 0x1F; /* Check the PHY 0 last */ + ULONG PhyIdLow, PhyIdHigh; + + if (!MiiRead(Adapter, PhyAddress, MII_PHY_ID1, &PhyIdLow)) + continue; + if (PhyIdLow == 0xFFFF) + continue; + + if (!MiiRead(Adapter, PhyAddress, MII_PHY_ID2, &PhyIdHigh)) + continue; + if (PhyIdHigh == 0xFFFF) + continue; + + Adapter->PhyAddress = PhyAddress; + Adapter->PhyModel = PhyIdHigh & PHYID2_MODEL_MASK; + Adapter->PhyOui = ((PhyIdLow & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT) | + ((PhyIdHigh & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT); + + /* Realtek hardcoded PhyIdLow to all zero's on certain PHYs */ + if (Adapter->PhyOui == PHY_OUI_REALTEK2) + Adapter->PhyOui = PHY_OUI_REALTEK; + + /* Setup PHY revision for Realtek */ + if (Adapter->PhyOui == PHY_OUI_REALTEK && Adapter->PhyModel == PHY_MODEL_REALTEK_8211) + { + ULONG PhyRevision; + + MiiRead(Adapter, PhyAddress, PHY_REALTEK_REVISION, &PhyRevision); + Adapter->PhyRevision = PhyRevision & PHY_REV_MASK; + } + + NDIS_DbgPrint(MIN_TRACE, ("Found PHY %X %X %X\n", + Adapter->PhyAddress, + Adapter->PhyModel, + Adapter->PhyOui)); + break; + } + if (Phy == 33) + { + return FALSE; + } + + return TRUE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SidebandUnitAcquireSemaphore( + _Inout_ PNVNET_ADAPTER Adapter) +{ + ULONG i; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + for (i = 10; i > 0; --i) + { + if ((NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_SEMA_MASK) == + NVREG_XMITCTL_MGMT_SEMA_FREE) + { + break; + } + + NdisMSleep(500000); + } + if (i == 0) + { + return FALSE; + } + + for (i = 0; i < 2; ++i) + { + ULONG TxControl = NV_READ(Adapter, NvRegTransmitterControl); + + NV_WRITE(Adapter, NvRegTransmitterControl, TxControl | NVREG_XMITCTL_HOST_SEMA_ACQ); + + /* Verify that the semaphore was acquired */ + TxControl = NV_READ(Adapter, NvRegTransmitterControl); + if (((TxControl & NVREG_XMITCTL_HOST_SEMA_MASK) == NVREG_XMITCTL_HOST_SEMA_ACQ) && + ((TxControl & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE)) + { + Adapter->Flags |= NV_UNIT_SEMAPHORE_ACQUIRED; + return TRUE; + } + + NdisStallExecution(50); + } + + return FALSE; +} + +VOID +SidebandUnitReleaseSemaphore( + _In_ PNVNET_ADAPTER Adapter) +{ + if (Adapter->Flags & NV_UNIT_SEMAPHORE_ACQUIRED) + { + ULONG TxControl; + + TxControl = NV_READ(Adapter, NvRegTransmitterControl); + TxControl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ; + NV_WRITE(Adapter, NvRegTransmitterControl, TxControl); + } +} + +static +CODE_SEG("PAGE") +BOOLEAN +SidebandUnitGetVersion( + _In_ PNVNET_ADAPTER Adapter, + _Out_ PULONG Version) +{ + ULONG i, DataReady, DataReady2; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + DataReady = NV_READ(Adapter, NvRegTransmitterControl); + + NV_WRITE(Adapter, NvRegMgmtUnitGetVersion, NVREG_MGMTUNITGETVERSION); + NV_WRITE(Adapter, NvRegTransmitterControl, DataReady ^ NVREG_XMITCTL_DATA_START); + + for (i = 100000; i > 0; --i) + { + DataReady2 = NV_READ(Adapter, NvRegTransmitterControl); + + if ((DataReady & NVREG_XMITCTL_DATA_READY) != (DataReady2 & NVREG_XMITCTL_DATA_READY)) + { + break; + } + + NdisStallExecution(50); + } + if (i == 0 || DataReady2 & NVREG_XMITCTL_DATA_ERROR) + { + return FALSE; + } + + *Version = NV_READ(Adapter, NvRegMgmtUnitVersion) & NVREG_MGMTUNITVERSION; + + return TRUE; +} + +static +BOOLEAN +MiiGetSpeedAndDuplex( + _In_ PNVNET_ADAPTER Adapter, + _Out_ PULONG MiiAdvertise, + _Out_ PULONG MiiLinkPartnerAbility, + _Out_ PULONG LinkSpeed, + _Out_ PBOOLEAN FullDuplex) +{ + ULONG MiiStatus, AdvLpa; + + *MiiAdvertise = 0; + *MiiLinkPartnerAbility = 0; + + /* Link status is a latched-low bit, read it twice */ + MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); + MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); + + /* Check link status */ + if (!(MiiStatus & MII_SR_LINK_STATUS)) + { + /* No link detected - configure NIC for 10 MB HD */ + *LinkSpeed = NVREG_LINKSPEED_10; + *FullDuplex = FALSE; + return FALSE; + } + + /* If we are forcing speed and duplex */ + if (Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX) + { + if (Adapter->Flags & NV_USER_SPEED_100) + { + *LinkSpeed = NVREG_LINKSPEED_100; + } + else + { + *LinkSpeed = NVREG_LINKSPEED_10; + } + *FullDuplex = !!(Adapter->Flags & NV_FORCE_FULL_DUPLEX); + return TRUE; + } + + /* Check auto-negotiation is complete */ + if (!(MiiStatus & MII_SR_AUTONEG_COMPLETE)) + { + /* Still in auto-negotiation - configure NIC for 10 MBit HD and wait */ + *LinkSpeed = NVREG_LINKSPEED_10; + *FullDuplex = FALSE; + return FALSE; + } + + MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiAdvertise); + MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_LINK_PARTNER, MiiLinkPartnerAbility); + + /* Gigabit ethernet */ + if (Adapter->Flags & NV_GIGABIT_PHY) + { + ULONG MiiControl1000, MiiStatus1000; + + MiiRead(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_CONTROL, &MiiControl1000); + MiiRead(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_STATUS, &MiiStatus1000); + + if ((MiiControl1000 & MII_MS_CR_1000T_FD) && (MiiStatus1000 & MII_MS_SR_1000T_FD)) + { + *LinkSpeed = NVREG_LINKSPEED_1000; + *FullDuplex = TRUE; + return TRUE; + } + } + + AdvLpa = (*MiiAdvertise) & (*MiiLinkPartnerAbility); + if (AdvLpa & MII_LP_100T_FD) + { + *LinkSpeed = NVREG_LINKSPEED_100; + *FullDuplex = TRUE; + } + else if (AdvLpa & MII_LP_100T_HD) + { + *LinkSpeed = NVREG_LINKSPEED_100; + *FullDuplex = FALSE; + } + else if (AdvLpa & MII_LP_10T_FD) + { + *LinkSpeed = NVREG_LINKSPEED_10; + *FullDuplex = TRUE; + } + else if (AdvLpa & MII_LP_10T_HD) + { + *LinkSpeed = NVREG_LINKSPEED_10; + *FullDuplex = FALSE; + } + else + { + *LinkSpeed = NVREG_LINKSPEED_10; + *FullDuplex = FALSE; + } + + return TRUE; +} + +static +VOID +NvNetSetSpeedAndDuplex( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG MiiAdvertise, + _In_ ULONG MiiLinkPartnerAbility) +{ + ULONG PhyRegister, TxDeferral, PauseFlags, MiiExpansion; + BOOLEAN RestartTransmitter = FALSE, RestartReceiver = FALSE; + + /* The transmitter and receiver must be restarted for safe update */ + if (NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_START) + { + RestartTransmitter = TRUE; + NvNetStopTransmitter(Adapter); + } + if (NV_READ(Adapter, NvRegReceiverControl) & NVREG_RCVCTL_START) + { + RestartReceiver = TRUE; + NvNetStopReceiver(Adapter); + } + + if (Adapter->Flags & NV_GIGABIT_PHY) + { + PhyRegister = NV_READ(Adapter, NvRegSlotTime); + PhyRegister &= ~NVREG_SLOTTIME_1000_FULL; + if ((Adapter->LinkSpeed == NVREG_LINKSPEED_10) || + (Adapter->LinkSpeed == NVREG_LINKSPEED_100)) + { + PhyRegister |= NVREG_SLOTTIME_10_100_FULL; + } + else if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) + { + PhyRegister |= NVREG_SLOTTIME_1000_FULL; + } + NV_WRITE(Adapter, NvRegSlotTime, PhyRegister); + } + + PhyRegister = NV_READ(Adapter, NvRegPhyInterface); + PhyRegister &= ~(PHY_HALF | PHY_100 | PHY_1000); + if (!Adapter->FullDuplex) + { + PhyRegister |= PHY_HALF; + } + if (Adapter->LinkSpeed == NVREG_LINKSPEED_100) + PhyRegister |= PHY_100; + else if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) + PhyRegister |= PHY_1000; + NV_WRITE(Adapter, NvRegPhyInterface, PhyRegister); + + /* Setup the deferral register */ + MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_EXPANSION, &MiiExpansion); + if (PhyRegister & PHY_RGMII) + { + if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) + { + TxDeferral = NVREG_TX_DEFERRAL_RGMII_1000; + } + else + { + if (!(MiiExpansion & MII_EXP_LP_AUTONEG) && !Adapter->FullDuplex && + (Adapter->Features & DEV_HAS_COLLISION_FIX)) + { + TxDeferral = NVREG_TX_DEFERRAL_RGMII_STRETCH_10; + } + else + { + TxDeferral = NVREG_TX_DEFERRAL_RGMII_STRETCH_100; + } + } + } + else + { + if (!(MiiExpansion & MII_EXP_LP_AUTONEG) && !Adapter->FullDuplex && + (Adapter->Features & DEV_HAS_COLLISION_FIX)) + { + TxDeferral = NVREG_TX_DEFERRAL_MII_STRETCH; + } + else + { + TxDeferral = NVREG_TX_DEFERRAL_DEFAULT; + } + } + NV_WRITE(Adapter, NvRegTxDeferral, TxDeferral); + + /* Setup the watermark register */ + if (Adapter->Features & (DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC)) + { + if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) + NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC2_3_1000); + else + NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC2_3_DEFAULT); + } + else + { + NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC1_DEFAULT); + } + + NV_WRITE(Adapter, NvRegMisc1, NVREG_MISC1_FORCE | (Adapter->FullDuplex ? 0 : NVREG_MISC1_HD)); + NV_WRITE(Adapter, NvRegLinkSpeed, Adapter->LinkSpeed | NVREG_LINKSPEED_FORCE); + + PauseFlags = 0; + + /* Setup pause frames */ + if (Adapter->FullDuplex) + { + if (!(Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX) && + (Adapter->PauseFlags & NV_PAUSEFRAME_AUTONEG)) + { + ULONG AdvPause = MiiAdvertise & (MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM); + ULONG LpaPause = MiiLinkPartnerAbility & (MII_LP_PAUSE_SYM | MII_LP_PAUSE_ASYM); + + switch (AdvPause) + { + case MII_ADV_PAUSE_SYM: + { + if (LpaPause & MII_LP_PAUSE_SYM) + { + PauseFlags |= NV_PAUSEFRAME_RX_ENABLE; + + if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_REQ) + PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; + } + break; + } + case MII_ADV_PAUSE_ASYM: + { + if (LpaPause == (MII_LP_PAUSE_SYM | MII_LP_PAUSE_ASYM)) + { + PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; + } + break; + } + case (MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM): + { + if (LpaPause & MII_LP_PAUSE_SYM) + { + PauseFlags |= NV_PAUSEFRAME_RX_ENABLE; + + if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_REQ) + PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; + } + if (LpaPause == MII_LP_PAUSE_ASYM) + { + PauseFlags |= NV_PAUSEFRAME_RX_ENABLE; + } + break; + } + + default: + break; + } + } + else + { + PauseFlags = Adapter->PauseFlags; + } + } + NvNetUpdatePauseFrame(Adapter, PauseFlags); + + if (RestartTransmitter) + { + NvNetStartTransmitter(Adapter); + } + if (RestartReceiver) + { + NvNetStartReceiver(Adapter); + } +} + +BOOLEAN +NvNetUpdateLinkSpeed( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG MiiAdvertise, MiiLinkPartnerAbility, LinkSpeed; + BOOLEAN FullDuplex, LinkUp; + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + LinkUp = MiiGetSpeedAndDuplex(Adapter, + &MiiAdvertise, + &MiiLinkPartnerAbility, + &LinkSpeed, + &FullDuplex); + if (Adapter->FullDuplex == FullDuplex && Adapter->LinkSpeed == LinkSpeed) + { + return LinkUp; + } + + NDIS_DbgPrint(MIN_TRACE, ("Configuring MAC from '%lx %s-duplex' to '%lx %s-duplex'\n", + Adapter->LinkSpeed, + Adapter->FullDuplex ? "full" : "half", + LinkSpeed, + FullDuplex ? "full" : "half")); + + Adapter->FullDuplex = FullDuplex; + Adapter->LinkSpeed = LinkSpeed; + + if (Adapter->Flags & NV_ACTIVE) + { + NdisDprAcquireSpinLock(&Adapter->Send.Lock); + NdisDprAcquireSpinLock(&Adapter->Receive.Lock); + } + + NvNetSetSpeedAndDuplex(Adapter, MiiAdvertise, MiiLinkPartnerAbility); + + if (Adapter->Flags & NV_ACTIVE) + { + NdisDprReleaseSpinLock(&Adapter->Receive.Lock); + NdisDprReleaseSpinLock(&Adapter->Send.Lock); + } + + return LinkUp; +} + +CODE_SEG("PAGE") +NDIS_STATUS +NvNetPhyInit( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG PhyState; + BOOLEAN RestorePhyState = FALSE, PhyInitialized = FALSE; + + PAGED_CODE(); + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + /* Take PHY and NIC out of low power mode */ + if (Adapter->Features & DEV_HAS_POWER_CNTRL) + { + ULONG PowerState = NV_READ(Adapter, NvRegPowerState2); + + PowerState &= ~NVREG_POWERSTATE2_POWERUP_MASK; + if ((Adapter->Features & DEV_NEED_LOW_POWER_FIX) && Adapter->RevisionId >= 0xA3) + { + PowerState |= NVREG_POWERSTATE2_POWERUP_REV_A3; + } + NV_WRITE(Adapter, NvRegPowerState2, PowerState); + } + + /* Clear PHY state and temporarily halt PHY interrupts */ + NV_WRITE(Adapter, NvRegMIIMask, 0); + PhyState = NV_READ(Adapter, NvRegAdapterControl); + if (PhyState & NVREG_ADAPTCTL_RUNNING) + { + RestorePhyState = TRUE; + + PhyState &= ~NVREG_ADAPTCTL_RUNNING; + NV_WRITE(Adapter, NvRegAdapterControl, PhyState); + } + NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_ALL); + + if (Adapter->Features & DEV_HAS_MGMT_UNIT) + { + ULONG UnitVersion; + + /* Management unit running on the MAC? */ + if ((NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST) && + (NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) && + SidebandUnitAcquireSemaphore(Adapter) && + SidebandUnitGetVersion(Adapter, &UnitVersion)) + { + if (UnitVersion > 0) + { + if (NV_READ(Adapter, NvRegMgmtUnitControl) & NVREG_MGMTUNITCONTROL_INUSE) + Adapter->Flags |= NV_MAC_IN_USE; + else + Adapter->Flags &= ~NV_MAC_IN_USE; + } + else + { + Adapter->Flags |= NV_MAC_IN_USE; + } + + NDIS_DbgPrint(MIN_TRACE, ("Management unit is running. MAC in use\n")); + + /* Management unit setup the PHY already? */ + if ((Adapter->Flags & NV_MAC_IN_USE) && + ((NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) == + NVREG_XMITCTL_SYNC_PHY_INIT)) + { + /* PHY is inited by management unit */ + PhyInitialized = TRUE; + + NDIS_DbgPrint(MIN_TRACE, ("PHY already initialized by management unit\n")); + } + } + } + + /* Find a suitable PHY */ + if (!FindPhyDevice(Adapter)) + { + NDIS_DbgPrint(MAX_TRACE, ("Could not find a valid PHY\n")); + goto Failure; + } + + /* We need to init the PHY */ + if (!PhyInitialized) + { + if (!PhyInit(Adapter)) + { + /* It's not critical for init, continue */ + } + } + else + { + ULONG MiiStatus; + + /* See if it is a gigabit PHY */ + MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); + if (MiiStatus & PHY_GIGABIT) + { + Adapter->Flags |= NV_GIGABIT_PHY; + } + } + + return NDIS_STATUS_SUCCESS; + +Failure: + if (RestorePhyState) + { + NV_WRITE(Adapter, NvRegAdapterControl, PhyState | NVREG_ADAPTCTL_RUNNING); + } + + return NDIS_STATUS_FAILURE; +} diff --git a/drivers/network/dd/nvnet/phyreg.h b/drivers/network/dd/nvnet/phyreg.h new file mode 100644 index 00000000000..8123b84f8d0 --- /dev/null +++ b/drivers/network/dd/nvnet/phyreg.h @@ -0,0 +1,42 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: PHY layer register definitions + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +#pragma once + +/* IEEE 802.3 */ +#define MII_CONTROL 0x00 +#define MII_CR_AUTONEG_RESTART 0x0200 +#define MII_CR_POWER_DOWN 0x0800 +#define MII_CR_AUTONEG 0x1000 +#define MII_CR_RESET 0x8000 +#define MII_STATUS 0x01 +#define MII_SR_LINK_STATUS 0x0004 +#define MII_SR_AUTONEG_COMPLETE 0x0020 +#define MII_PHY_ID1 0x02 +#define MII_PHY_ID2 0x03 +#define MII_AUTONEG_ADVERTISE 0x04 +#define MII_ADV_10T_HD 0x0020 +#define MII_ADV_10T_FD 0x0040 +#define MII_ADV_100T_HD 0x0080 +#define MII_ADV_100T_FD 0x0100 +#define MII_ADV_100T4 0x0200 +#define MII_ADV_PAUSE_SYM 0x0400 +#define MII_ADV_PAUSE_ASYM 0x0800 +#define MII_AUTONEG_LINK_PARTNER 0x05 +#define MII_LP_10T_HD 0x0020 +#define MII_LP_10T_FD 0x0040 +#define MII_LP_100T_HD 0x0080 +#define MII_LP_100T_FD 0x0100 +#define MII_LP_PAUSE_SYM 0x0400 +#define MII_LP_PAUSE_ASYM 0x0800 +#define MII_AUTONEG_EXPANSION 0x06 +#define MII_EXP_LP_AUTONEG 0x0001 +#define MII_MASTER_SLAVE_CONTROL 0x09 +#define MII_MS_CR_1000T_HD 0x0100 +#define MII_MS_CR_1000T_FD 0x0200 +#define MII_MASTER_SLAVE_STATUS 0x0A +#define MII_MS_SR_1000T_FD 0x0800 diff --git a/drivers/network/dd/nvnet/requests.c b/drivers/network/dd/nvnet/requests.c new file mode 100644 index 00000000000..4cf57fed230 --- /dev/null +++ b/drivers/network/dd/nvnet/requests.c @@ -0,0 +1,1505 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Miniport information callbacks + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "nvnet.h" + +#define NDEBUG +#include "debug.h" + +/* GLOBALS ********************************************************************/ + +static const NDIS_OID NvpSupportedOidList[] = +{ + OID_GEN_SUPPORTED_LIST, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_VLAN_ID, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* Statistics */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, + + /* Offload */ + OID_TCP_TASK_OFFLOAD, + + /* Power management */ + OID_PNP_CAPABILITIES, + OID_PNP_SET_POWER, + OID_PNP_QUERY_POWER, + OID_PNP_ADD_WAKE_UP_PATTERN, + OID_PNP_REMOVE_WAKE_UP_PATTERN, + OID_PNP_ENABLE_WAKE_UP +}; + +/* FUNCTIONS ******************************************************************/ + +static +ULONG +NvNetGetLinkSpeed( + _In_ PNVNET_ADAPTER Adapter) +{ + ULONG LinkSpeedMbps; + + switch (Adapter->LinkSpeed) + { + case NVREG_LINKSPEED_10: + LinkSpeedMbps = 10; + break; + case NVREG_LINKSPEED_100: + LinkSpeedMbps = 100; + break; + case NVREG_LINKSPEED_1000: + LinkSpeedMbps = 1000; + break; + + default: + UNREACHABLE; + break; + } + + return LinkSpeedMbps; +} + +static +ULONG +PacketFilterToMask( + _In_ ULONG PacketFilter) +{ + ULONG FilterMask = NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR | NVREG_PFF_PROMISC; + + if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) + { + FilterMask &= ~NVREG_PFF_MYADDR; + } + + if (PacketFilter & NDIS_PACKET_TYPE_BROADCAST) + { + FilterMask &= ~NVREG_PFF_PROMISC; + } + + return FilterMask; +} + +static +DECLSPEC_NOINLINE /* Called from pageable code */ +VOID +NvNetApplyPacketFilter( + _In_ PNVNET_ADAPTER Adapter) +{ + UCHAR Address[ETH_LENGTH_OF_ADDRESS]; + UCHAR Mask[ETH_LENGTH_OF_ADDRESS]; + ULONG FilterMask; + BOOLEAN RestartReceiver; + + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) + { + NdisZeroMemory(Address, sizeof(Address)); + NdisZeroMemory(Mask, sizeof(Mask)); + + Address[0] |= NVREG_MCASTADDRA_FORCE; + Mask[0] |= NVREG_MCASTADDRA_FORCE; + } + else if (Adapter->PacketFilter & NDIS_PACKET_TYPE_MULTICAST) + { + if (Adapter->MulticastListSize > 0) + { + ULONG i, j; + + NdisFillMemory(Address, sizeof(Address), 0xFF); + NdisFillMemory(Mask, sizeof(Mask), 0xFF); + + for (i = 0; i < Adapter->MulticastListSize; ++i) + { + PUCHAR MacAddress = Adapter->MulticastList[i].MacAddress; + + for (j = 0; j < ETH_LENGTH_OF_ADDRESS; ++j) + { + Address[j] &= MacAddress[j]; + Mask[j] &= ~MacAddress[j]; + } + } + + for (j = 0; j < ETH_LENGTH_OF_ADDRESS; ++j) + { + Mask[j] |= Address[j]; + } + } + else + { + NdisZeroMemory(Address, sizeof(Address)); + NdisZeroMemory(Mask, sizeof(Mask)); + } + } + else + { + NdisZeroMemory(Address, sizeof(Address)); + NdisFillMemory(Mask, sizeof(Mask), 0xFF); + } + + FilterMask = NV_READ(Adapter, NvRegPacketFilterFlags) & NVREG_PFF_PAUSE_RX; + FilterMask |= PacketFilterToMask(Adapter->PacketFilter); + + NdisAcquireSpinLock(&Adapter->Receive.Lock); + + RestartReceiver = !!(NV_READ(Adapter, NvRegReceiverControl) & NVREG_RCVCTL_START); + if (RestartReceiver) + { + NvNetStopReceiver(Adapter); + } + + NV_WRITE(Adapter, NvRegMulticastAddrA, + Address[3] << 24 | Address[2] << 16 | Address[1] << 8 | Address[0]); + NV_WRITE(Adapter, NvRegMulticastAddrB, Address[5] << 8 | Address[4]); + NV_WRITE(Adapter, NvRegMulticastMaskA, + Mask[3] << 24 | Mask[2] << 16 | Mask[1] << 8 | Mask[0]); + NV_WRITE(Adapter, NvRegMulticastMaskB, Mask[5] << 8 | Mask[4]); + + NV_WRITE(Adapter, NvRegPacketFilterFlags, FilterMask); + + if (RestartReceiver) + { + NvNetStartReceiver(Adapter); + } + + NdisReleaseSpinLock(&Adapter->Receive.Lock); +} + +static +VOID +NvNetReadStatistics( + _In_ PNVNET_ADAPTER Adapter) +{ + Adapter->Statistics.HwTxCnt += NV_READ(Adapter, NvRegTxCnt); + Adapter->Statistics.HwTxZeroReXmt += NV_READ(Adapter, NvRegTxZeroReXmt); + Adapter->Statistics.HwTxOneReXmt += NV_READ(Adapter, NvRegTxOneReXmt); + Adapter->Statistics.HwTxManyReXmt += NV_READ(Adapter, NvRegTxManyReXmt); + Adapter->Statistics.HwTxLateCol += NV_READ(Adapter, NvRegTxLateCol); + Adapter->Statistics.HwTxUnderflow += NV_READ(Adapter, NvRegTxUnderflow); + Adapter->Statistics.HwTxLossCarrier += NV_READ(Adapter, NvRegTxLossCarrier); + Adapter->Statistics.HwTxExcessDef += NV_READ(Adapter, NvRegTxExcessDef); + Adapter->Statistics.HwTxRetryErr += NV_READ(Adapter, NvRegTxRetryErr); + Adapter->Statistics.HwRxFrameErr += NV_READ(Adapter, NvRegRxFrameErr); + Adapter->Statistics.HwRxExtraByte += NV_READ(Adapter, NvRegRxExtraByte); + Adapter->Statistics.HwRxLateCol += NV_READ(Adapter, NvRegRxLateCol); + Adapter->Statistics.HwRxRunt += NV_READ(Adapter, NvRegRxRunt); + Adapter->Statistics.HwRxFrameTooLong += NV_READ(Adapter, NvRegRxFrameTooLong); + Adapter->Statistics.HwRxOverflow += NV_READ(Adapter, NvRegRxOverflow); + Adapter->Statistics.HwRxFCSErr += NV_READ(Adapter, NvRegRxFCSErr); + Adapter->Statistics.HwRxFrameAlignErr += NV_READ(Adapter, NvRegRxFrameAlignErr); + Adapter->Statistics.HwRxLenErr += NV_READ(Adapter, NvRegRxLenErr); + Adapter->Statistics.HwRxUnicast += NV_READ(Adapter, NvRegRxUnicast); + Adapter->Statistics.HwRxMulticast += NV_READ(Adapter, NvRegRxMulticast); + Adapter->Statistics.HwRxBroadcast += NV_READ(Adapter, NvRegRxBroadcast); + + if (Adapter->Features & DEV_HAS_STATISTICS_V2) + { + Adapter->Statistics.HwTxDef += NV_READ(Adapter, NvRegTxDef); + Adapter->Statistics.HwTxFrame += NV_READ(Adapter, NvRegTxFrame); + Adapter->Statistics.HwRxCnt += NV_READ(Adapter, NvRegRxCnt); + Adapter->Statistics.HwTxPause += NV_READ(Adapter, NvRegTxPause); + Adapter->Statistics.HwRxPause += NV_READ(Adapter, NvRegRxPause); + Adapter->Statistics.HwRxDropFrame += NV_READ(Adapter, NvRegRxDropFrame); + } + + if (Adapter->Features & DEV_HAS_STATISTICS_V3) + { + Adapter->Statistics.HwTxUnicast += NV_READ(Adapter, NvRegTxUnicast); + Adapter->Statistics.HwTxMulticast += NV_READ(Adapter, NvRegTxMulticast); + Adapter->Statistics.HwTxBroadcast += NV_READ(Adapter, NvRegTxBroadcast); + } +} + +static +VOID +NvNetQueryHwCounter( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_OID Oid, + _Out_ PULONG64 Counter) +{ + switch (Oid) + { + case OID_GEN_XMIT_OK: + *Counter = (Adapter->Features & DEV_HAS_STATISTICS_V2) + ? Adapter->Statistics.HwTxFrame + : (Adapter->Statistics.HwTxZeroReXmt + + Adapter->Statistics.HwTxOneReXmt + + Adapter->Statistics.HwTxManyReXmt); + break; + case OID_GEN_RCV_OK: + *Counter = (Adapter->Statistics.HwRxUnicast + + Adapter->Statistics.HwRxMulticast + + Adapter->Statistics.HwRxBroadcast); + break; + case OID_GEN_XMIT_ERROR: + *Counter = (Adapter->Statistics.HwTxRetryErr + + Adapter->Statistics.HwTxLateCol + + Adapter->Statistics.HwTxUnderflow + + Adapter->Statistics.HwTxLossCarrier + + Adapter->Statistics.HwTxExcessDef); + break; + case OID_GEN_RCV_ERROR: + *Counter = (Adapter->Statistics.HwRxFrameAlignErr + + Adapter->Statistics.HwRxLenErr + + Adapter->Statistics.HwRxRunt + + Adapter->Statistics.HwRxFrameTooLong + + Adapter->Statistics.HwRxFCSErr + + Adapter->Statistics.HwRxFrameErr + + Adapter->Statistics.HwRxExtraByte + + Adapter->Statistics.HwRxLateCol); + break; + case OID_GEN_RCV_NO_BUFFER: + *Counter = (Adapter->Statistics.HwRxDropFrame + + Adapter->Statistics.HwRxOverflow + + Adapter->Statistics.ReceiveIrqNoBuffers); + break; + case OID_GEN_DIRECTED_FRAMES_RCV: + *Counter = Adapter->Statistics.HwRxUnicast; + break; + case OID_GEN_RCV_CRC_ERROR: + *Counter = Adapter->Statistics.HwRxFCSErr; + break; + case OID_802_3_RCV_ERROR_ALIGNMENT: + *Counter = Adapter->Statistics.HwRxFrameErr; + break; + case OID_802_3_XMIT_ONE_COLLISION: + *Counter = Adapter->Statistics.HwTxOneReXmt; + break; + case OID_802_3_XMIT_MORE_COLLISIONS: + *Counter = Adapter->Statistics.HwTxManyReXmt; + break; + case OID_802_3_XMIT_DEFERRED: + *Counter = Adapter->Statistics.HwTxDef; + break; + case OID_802_3_XMIT_MAX_COLLISIONS: + *Counter = Adapter->Statistics.HwTxRetryErr; + break; + case OID_802_3_RCV_OVERRUN: + *Counter = Adapter->Statistics.HwRxOverflow; + break; + case OID_802_3_XMIT_UNDERRUN: + *Counter = Adapter->Statistics.HwTxUnderflow; + break; + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + *Counter = Adapter->Statistics.HwTxZeroReXmt; + break; + case OID_802_3_XMIT_TIMES_CRS_LOST: + *Counter = Adapter->Statistics.HwTxLossCarrier; + break; + case OID_802_3_XMIT_LATE_COLLISIONS: + *Counter = Adapter->Statistics.HwTxLateCol; + break; + + default: + UNREACHABLE; + break; + } +} + +static +VOID +NvNetQuerySoftwareCounter( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_OID Oid, + _Out_ PULONG64 Counter) +{ + switch (Oid) + { + case OID_GEN_XMIT_OK: + *Counter = Adapter->Statistics.TransmitOk; + break; + case OID_GEN_RCV_OK: + *Counter = Adapter->Statistics.ReceiveOk; + break; + case OID_GEN_XMIT_ERROR: + *Counter = Adapter->Statistics.TransmitErrors; + break; + case OID_GEN_RCV_ERROR: + *Counter = Adapter->Statistics.ReceiveErrors; + break; + case OID_GEN_RCV_NO_BUFFER: + *Counter = Adapter->Statistics.ReceiveNoBuffers; + break; + case OID_GEN_DIRECTED_FRAMES_RCV: + *Counter = 0; + break; + case OID_GEN_RCV_CRC_ERROR: + *Counter = Adapter->Statistics.ReceiveCrcErrors; + break; + case OID_802_3_RCV_ERROR_ALIGNMENT: + *Counter = Adapter->Statistics.ReceiveAlignmentErrors; + break; + case OID_802_3_XMIT_ONE_COLLISION: + *Counter = Adapter->Statistics.TransmitOneRetry; + break; + case OID_802_3_XMIT_MORE_COLLISIONS: + *Counter = (Adapter->Statistics.TransmitOk - + Adapter->Statistics.TransmitOneRetry - + Adapter->Statistics.TransmitZeroRetry); + break; + case OID_802_3_XMIT_DEFERRED: + *Counter = Adapter->Statistics.TransmitDeferred; + break; + case OID_802_3_XMIT_MAX_COLLISIONS: + *Counter = Adapter->Statistics.TransmitExcessiveCollisions; + break; + case OID_802_3_RCV_OVERRUN: + *Counter = Adapter->Statistics.ReceiveOverrunErrors; + break; + case OID_802_3_XMIT_UNDERRUN: + *Counter = Adapter->Statistics.TransmitUnderrunErrors; + break; + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + *Counter = Adapter->Statistics.TransmitZeroRetry; + break; + case OID_802_3_XMIT_TIMES_CRS_LOST: + *Counter = Adapter->Statistics.TransmitLostCarrierSense; + break; + case OID_802_3_XMIT_LATE_COLLISIONS: + *Counter = Adapter->Statistics.TransmitLateCollisions; + break; + + default: + UNREACHABLE; + break; + } +} + +static +NDIS_STATUS +NvNetFillPowerManagementCapabilities( + _In_ PNVNET_ADAPTER Adapter, + _Out_ PNDIS_PNP_CAPABILITIES Capabilities) +{ + Capabilities->WakeUpCapabilities.MinMagicPacketWakeUp = + Capabilities->WakeUpCapabilities.MinPatternWakeUp = + Capabilities->WakeUpCapabilities.MinLinkChangeWakeUp = NdisDeviceStateD3; + + /* All hardware is PM-aware */ + return NDIS_STATUS_SUCCESS; +} + +static +ULONG +BuildFrameSignature( + _In_ PNVNET_WAKE_FRAME WakeFrame) +{ + ULONG i, j, Crc; + + Crc = 0xFFFFFFFF; + for (i = 0; i < sizeof(WakeFrame->WakeUpPattern); ++i) + { + if (WakeFrame->PatternMask.AsUCHAR[i / 8] & (1 << (i % 8))) + { + Crc ^= WakeFrame->WakeUpPattern[i]; + for (j = 8; j > 0; --j) + { + Crc = (Crc >> 1) ^ (-(LONG)(Crc & 1) & 0xEDB88320); + } + } + } + + return ~Crc; +} + +static +VOID +WriteWakeFrame( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNVNET_WAKE_FRAME WakeFrame, + _In_ ULONG FrameNumber) +{ + ULONG Offset = FrameNumber * 5 * sizeof(ULONG); + + if (FrameNumber >= NV_WAKEUPPATTERNS) + { + Offset += NV_PATTERN_V2_OFFSET; + } + + NV_WRITE(Adapter, NvRegPatternCrc + Offset, BuildFrameSignature(WakeFrame)); + NV_WRITE(Adapter, NvRegPatternMask0 + Offset, WakeFrame->PatternMask.AsULONG[0]); + NV_WRITE(Adapter, NvRegPatternMask1 + Offset, WakeFrame->PatternMask.AsULONG[1]); + NV_WRITE(Adapter, NvRegPatternMask2 + Offset, WakeFrame->PatternMask.AsULONG[2]); + NV_WRITE(Adapter, NvRegPatternMask3 + Offset, WakeFrame->PatternMask.AsULONG[3]); +} + +static +ULONG +FrameNumberToWakeUpMask( + _In_ ULONG FrameNumber) +{ + if (FrameNumber < 5) + return 0x10000 << FrameNumber; + else + return 0; +} + +static +ULONG +FrameNumberToPowerMask( + _In_ ULONG FrameNumber) +{ + switch (FrameNumber) + { + case 5: + return NVREG_POWERSTATE2_WAKEUPPAT_5; + case 6: + return NVREG_POWERSTATE2_WAKEUPPAT_6; + case 7: + return NVREG_POWERSTATE2_WAKEUPPAT_7; + + default: + return 0; + } +} + +VOID +NvNetSetPowerState( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE NewPowerState, + _In_ ULONG WakeFlags) +{ + ULONG i, PowerState, PowerState2, WakeUpFlags; + + NV_READ(Adapter, NvRegPowerCap); + + WakeUpFlags = 0; + PowerState2 = 0; + if (Adapter->Features & DEV_HAS_POWER_CNTRL) + { + PowerState2 = NV_READ(Adapter, NvRegPowerState2); + PowerState2 &= ~(NVREG_POWERSTATE2_WAKEUPPAT_5 | + NVREG_POWERSTATE2_WAKEUPPAT_6 | + NVREG_POWERSTATE2_WAKEUPPAT_7); + } + + if (NewPowerState != NdisDeviceStateD0) + { + ULONG FramesEnabled = 0; + + if (WakeFlags & NDIS_PNP_WAKE_UP_PATTERN_MATCH) + WakeUpFlags |= NVREG_WAKEUPFLAGS_ENABLE_MAGPAT; + if (WakeFlags & NDIS_PNP_WAKE_UP_LINK_CHANGE) + WakeUpFlags |= NVREG_WAKEUPFLAGS_ENABLE_LINKCHANGE; + if (WakeFlags & NDIS_PNP_WAKE_UP_MAGIC_PACKET) + { + WakeUpFlags |= NVREG_WAKEUPFLAGS_ENABLE_WAKEUPPAT; + + for (i = 0; i < RTL_NUMBER_OF(Adapter->WakeFrames); ++i) + { + PNVNET_WAKE_FRAME WakeFrame = Adapter->WakeFrames[i]; + + if (!WakeFrame) + continue; + + WriteWakeFrame(Adapter, WakeFrame, i); + + PowerState2 |= FrameNumberToPowerMask(i); + WakeUpFlags |= FrameNumberToWakeUpMask(i); + + ++FramesEnabled; + } + } + + if (WakeUpFlags) + { + if (!(Adapter->Flags & NV_MAC_IN_USE)) + { + PowerState2 &= ~NVREG_POWERSTATE2_GATE_CLOCKS; + PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCK_3; + + if (!FramesEnabled && (WakeUpFlags & NVREG_WAKEUPFLAGS_ENABLE_LINKCHANGE)) + PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCK_1; + if (FramesEnabled < NV_WAKEUPMASKENTRIES) + PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCK_2; + } + + NvNetStartReceiver(Adapter); + } + else + { + if (!(Adapter->Flags & NV_MAC_IN_USE)) + PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCKS; + } + } + + NdisStallExecution(NV_POWER_STALL); + + NV_WRITE(Adapter, NvRegWakeUpFlags, WakeUpFlags); + if (Adapter->Features & DEV_HAS_POWER_CNTRL) + { + NV_WRITE(Adapter, NvRegPowerState2, PowerState2); + } + + NV_WRITE(Adapter, NvRegPowerState, + NV_READ(Adapter, NvRegPowerState) | NVREG_POWERSTATE_POWEREDUP); + for (i = 0; i < NV_POWER_ATTEMPTS; ++i) + { + ULONG State = NV_READ(Adapter, NvRegPowerState); + + if (!(State & NVREG_POWERSTATE_POWEREDUP)) + break; + + NV_WRITE(Adapter, NvRegPowerState, State | NVREG_POWERSTATE_POWEREDUP); + + NdisStallExecution(NV_POWER_DELAY); + } + + PowerState = NewPowerState - 1; + if (WakeUpFlags) + { + PowerState |= NVREG_POWERSTATE_VALID; + } + NV_WRITE(Adapter, NvRegPowerState, PowerState); +} + +static +CODE_SEG("PAGE") +VOID +NTAPI +NvNetPowerWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context) +{ + PNVNET_ADAPTER Adapter = Context; + + UNREFERENCED_PARAMETER(WorkItem); + + PAGED_CODE(); + + if (Adapter->PowerStatePending == NdisDeviceStateD0) + { + NvNetSetPowerState(Adapter, NdisDeviceStateD0, 0); + + NT_VERIFY(NvNetInitNIC(Adapter, TRUE) == NDIS_STATUS_SUCCESS); + + NvNetStartAdapter(Adapter); + + NvNetApplyPacketFilter(Adapter); + } + else + { + NvNetPauseProcessing(Adapter); + + NvNetStopAdapter(Adapter); + + NvNetIdleTransmitter(Adapter, FALSE); + NvNetStopTransmitter(Adapter); + NvNetStopReceiver(Adapter); + NV_WRITE(Adapter, NvRegTxRxControl, + Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET); + NdisStallExecution(NV_TXRX_RESET_DELAY); + + NvNetFlushTransmitQueue(Adapter, NDIS_STATUS_FAILURE); + + NvNetSetPowerState(Adapter, Adapter->PowerStatePending, Adapter->WakeFlags); + } + + NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS); +} + +static +NDIS_STATUS +NvNetSetPower( + _In_ PNVNET_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE NewPowerState) +{ + Adapter->PowerStatePending = NewPowerState; + + NdisInitializeWorkItem(&Adapter->PowerWorkItem, NvNetPowerWorker, Adapter); + NdisScheduleWorkItem(&Adapter->PowerWorkItem); + + return NDIS_STATUS_PENDING; +} + +static +NDIS_STATUS +NvNetAddWakeUpPattern( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PM_PACKET_PATTERN Pattern) +{ + ULONG FrameNumber; + NDIS_STATUS Status; + PNVNET_WAKE_FRAME WakeFrame; + + if (!_BitScanForward(&FrameNumber, Adapter->WakeFrameBitmap)) + { + return NDIS_STATUS_RESOURCES; + } + + Status = NdisAllocateMemoryWithTag((PVOID*)&WakeFrame, sizeof(*WakeFrame), NVNET_TAG); + if (Status != NDIS_STATUS_SUCCESS) + { + return NDIS_STATUS_RESOURCES; + } + + Adapter->WakeFrameBitmap &= ~(1 << FrameNumber); + + NdisZeroMemory(WakeFrame, sizeof(*WakeFrame)); + NdisMoveMemory(&WakeFrame->PatternMask, + (PUCHAR)Pattern + sizeof(NDIS_PM_PACKET_PATTERN), + min(Pattern->MaskSize, 16)); + NdisMoveMemory(&WakeFrame->WakeUpPattern, + (PUCHAR)Pattern + Pattern->PatternOffset, + min(Pattern->PatternSize, 128)); + Adapter->WakeFrames[FrameNumber] = WakeFrame; + + /* TODO: VLAN frame translation */ + + return NDIS_STATUS_SUCCESS; +} + +static +BOOLEAN +NvEqualMemory( + _In_reads_bytes_(Length) PVOID Destination, + _In_reads_bytes_(Length) PVOID Source, + _In_ ULONG Length) +{ + ULONG i; + PUCHAR Src, Dest; + + Src = Source; + Dest = Destination; + for (i = 0; i < Length; ++i) + { + if (Src[i] != Dest[i]) + return FALSE; + } + + return TRUE; +} +/* 'memcmp' is unavailable for some reason */ +#undef NdisEqualMemory +#define NdisEqualMemory NvEqualMemory + +static +NDIS_STATUS +NvNetRemoveWakeUpPattern( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PM_PACKET_PATTERN Pattern) +{ + ULONG i; + + for (i = 0; i < RTL_NUMBER_OF(Adapter->WakeFrames); ++i) + { + PNVNET_WAKE_FRAME WakeFrame = Adapter->WakeFrames[i]; + + if (!WakeFrame) + continue; + + if (!NdisEqualMemory(&WakeFrame->PatternMask, + (PUCHAR)Pattern + sizeof(NDIS_PM_PACKET_PATTERN), + min(Pattern->MaskSize, 16))) + { + continue; + } + + if (!NdisEqualMemory(&WakeFrame->WakeUpPattern, + (PUCHAR)Pattern + Pattern->PatternOffset, + min(Pattern->PatternSize, 128))) + { + continue; + } + + NdisFreeMemory(WakeFrame, sizeof(*WakeFrame), 0); + + Adapter->WakeFrameBitmap |= (1 << i); + Adapter->WakeFrames[i] = NULL; + + return NDIS_STATUS_SUCCESS; + } + + return NDIS_STATUS_INVALID_DATA; +} + +static +ULONG +NvNetGetWakeUp( + _In_ PNVNET_ADAPTER Adapter) +{ + return Adapter->WakeFlags & (NDIS_PNP_WAKE_UP_MAGIC_PACKET | + NDIS_PNP_WAKE_UP_PATTERN_MATCH | + NDIS_PNP_WAKE_UP_LINK_CHANGE); +} + +static +VOID +NvNetEnableWakeUp( + _In_ PNVNET_ADAPTER Adapter, + _In_ ULONG Flags) +{ + Adapter->WakeFlags = Flags; +} + +static +NDIS_STATUS +NvNetGetTcpTaskOffload( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesWritten, + _Out_ PULONG BytesNeeded) +{ + ULONG InfoLength; + PNDIS_TASK_OFFLOAD TaskOffload; + + if (!(Adapter->Flags & (NV_SEND_CHECKSUM | NV_SEND_LARGE_SEND))) + { + *BytesWritten = 0; + *BytesNeeded = 0; + return NDIS_STATUS_NOT_SUPPORTED; + } + + InfoLength = sizeof(NDIS_TASK_OFFLOAD_HEADER); + if (Adapter->Flags & NV_SEND_CHECKSUM) + { + InfoLength += FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + + sizeof(NDIS_TASK_TCP_IP_CHECKSUM); + } + if (Adapter->Flags & NV_SEND_LARGE_SEND) + { + InfoLength += FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + + sizeof(NDIS_TASK_TCP_LARGE_SEND); + } + + if (InformationBufferLength < InfoLength) + { + *BytesWritten = 0; + *BytesNeeded = InfoLength; + return NDIS_STATUS_BUFFER_TOO_SHORT; + } + + if ((TaskOffloadHeader->EncapsulationFormat.Encapsulation != IEEE_802_3_Encapsulation) && + (TaskOffloadHeader->EncapsulationFormat.Encapsulation != UNSPECIFIED_Encapsulation || + TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize != sizeof(ETH_HEADER))) + { + *BytesWritten = 0; + *BytesNeeded = 0; + return NDIS_STATUS_NOT_SUPPORTED; + } + if (TaskOffloadHeader->Version != NDIS_TASK_OFFLOAD_VERSION) + { + *BytesWritten = 0; + *BytesNeeded = 0; + return NDIS_STATUS_NOT_SUPPORTED; + } + + TaskOffloadHeader->OffsetFirstTask = sizeof(NDIS_TASK_OFFLOAD_HEADER); + TaskOffload = (PNDIS_TASK_OFFLOAD)(TaskOffloadHeader + 1); + if (Adapter->Flags & NV_SEND_CHECKSUM) + { + PNDIS_TASK_TCP_IP_CHECKSUM ChecksumTask; + + TaskOffload->Size = sizeof(NDIS_TASK_OFFLOAD); + TaskOffload->Version = NDIS_TASK_OFFLOAD_VERSION; + TaskOffload->Task = TcpIpChecksumNdisTask; + TaskOffload->TaskBufferLength = sizeof(NDIS_TASK_TCP_IP_CHECKSUM); + TaskOffload->OffsetNextTask = FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + + sizeof(NDIS_TASK_TCP_IP_CHECKSUM); + + ChecksumTask = (PNDIS_TASK_TCP_IP_CHECKSUM)TaskOffload->TaskBuffer; + NdisZeroMemory(ChecksumTask, sizeof(*ChecksumTask)); + + ChecksumTask->V4Transmit.IpOptionsSupported = 1; + ChecksumTask->V4Transmit.TcpOptionsSupported = 1; + ChecksumTask->V4Transmit.TcpChecksum = 1; + ChecksumTask->V4Transmit.UdpChecksum = 1; + ChecksumTask->V4Transmit.IpChecksum = 1; + + ChecksumTask->V4Receive.IpOptionsSupported = 1; + ChecksumTask->V4Receive.TcpOptionsSupported = 1; + ChecksumTask->V4Receive.TcpChecksum = 1; + ChecksumTask->V4Receive.UdpChecksum = 1; + ChecksumTask->V4Receive.IpChecksum = 1; + + TaskOffload = (PNDIS_TASK_OFFLOAD)(ChecksumTask + 1); + } + if (Adapter->Flags & NV_SEND_LARGE_SEND) + { + PNDIS_TASK_TCP_LARGE_SEND LargeSendTask; + + TaskOffload->Size = sizeof(NDIS_TASK_OFFLOAD); + TaskOffload->Version = NDIS_TASK_OFFLOAD_VERSION; + TaskOffload->Task = TcpLargeSendNdisTask; + TaskOffload->TaskBufferLength = sizeof(NDIS_TASK_TCP_LARGE_SEND); + TaskOffload->OffsetNextTask = 0; + + LargeSendTask = (PNDIS_TASK_TCP_LARGE_SEND)TaskOffload->TaskBuffer; + LargeSendTask->Version = NDIS_TASK_TCP_LARGE_SEND_V0; + LargeSendTask->MinSegmentCount = NVNET_MINIMUM_LSO_SEGMENT_COUNT; + LargeSendTask->MaxOffLoadSize = NVNET_MAXIMUM_LSO_FRAME_SIZE; + LargeSendTask->IpOptions = TRUE; + LargeSendTask->TcpOptions = TRUE; + } + TaskOffload->OffsetNextTask = 0; + + *BytesWritten = InfoLength; + *BytesNeeded = 0; + + return NDIS_STATUS_SUCCESS; +} + +static +NDIS_STATUS +NvNetSetTcpTaskOffload( + _Inout_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader, + _In_ PULONG BytesRead) +{ + ULONG Offset; + PNDIS_TASK_OFFLOAD TaskOffload; + + if (TaskOffloadHeader->Version != NDIS_TASK_OFFLOAD_VERSION) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + Adapter->IpHeaderOffset = TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize; + + TaskOffload = (PNDIS_TASK_OFFLOAD)TaskOffloadHeader; + Offset = TaskOffloadHeader->OffsetFirstTask; + + while (Offset) + { + *BytesRead += FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer); + + TaskOffload = (PNDIS_TASK_OFFLOAD)((PUCHAR)TaskOffload + Offset); + switch (TaskOffload->Task) + { + case TcpIpChecksumNdisTask: + { + PNDIS_TASK_TCP_IP_CHECKSUM Task; + + *BytesRead += sizeof(NDIS_TASK_TCP_IP_CHECKSUM); + + if (!(Adapter->Flags & NV_SEND_CHECKSUM)) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + Task = (PNDIS_TASK_TCP_IP_CHECKSUM)TaskOffload->TaskBuffer; + + Adapter->Offload.SendTcpChecksum = Task->V4Transmit.TcpChecksum; + Adapter->Offload.SendUdpChecksum = Task->V4Transmit.UdpChecksum; + Adapter->Offload.SendIpChecksum = Task->V4Transmit.IpChecksum; + + Adapter->Offload.ReceiveTcpChecksum = Task->V4Receive.TcpChecksum; + Adapter->Offload.ReceiveUdpChecksum = Task->V4Receive.UdpChecksum; + Adapter->Offload.ReceiveIpChecksum = Task->V4Receive.IpChecksum; + break; + } + + case TcpLargeSendNdisTask: + { + PNDIS_TASK_TCP_LARGE_SEND Task; + + if (!(Adapter->Flags & NV_SEND_LARGE_SEND)) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + if ((TaskOffloadHeader-> + EncapsulationFormat.Encapsulation != IEEE_802_3_Encapsulation) && + (TaskOffloadHeader-> + EncapsulationFormat.Encapsulation != UNSPECIFIED_Encapsulation || + TaskOffloadHeader-> + EncapsulationFormat.EncapsulationHeaderSize != sizeof(ETH_HEADER))) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + *BytesRead += sizeof(NDIS_TASK_TCP_LARGE_SEND); + + Task = (PNDIS_TASK_TCP_LARGE_SEND)TaskOffload->TaskBuffer; + + if (Task->MinSegmentCount != NVNET_MINIMUM_LSO_SEGMENT_COUNT) + return NDIS_STATUS_NOT_SUPPORTED; + + if (Task->MaxOffLoadSize > NVNET_MAXIMUM_LSO_FRAME_SIZE) + return NDIS_STATUS_NOT_SUPPORTED; + + /* Nothing to do */ + break; + } + + default: + break; + } + + Offset = TaskOffload->OffsetNextTask; + } + + NdisAcquireSpinLock(&Adapter->Send.Lock); + + if (Adapter->Offload.ReceiveTcpChecksum || + Adapter->Offload.ReceiveUdpChecksum || + Adapter->Offload.ReceiveIpChecksum) + { + Adapter->TxRxControl |= NVREG_TXRXCTL_RXCHECK; + } + else + { + Adapter->TxRxControl &= ~NVREG_TXRXCTL_RXCHECK; + } + NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl); + + NdisReleaseSpinLock(&Adapter->Send.Lock); + + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS +NTAPI +MiniportQueryInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesWritten, + _Out_ PULONG BytesNeeded) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + ULONG InfoLength; + PVOID InfoPtr; + union _GENERIC_INFORMATION + { + USHORT Ushort; + ULONG Ulong; + ULONG64 Ulong64; + NDIS_MEDIUM Medium; + NDIS_HARDWARE_STATUS Status; + NDIS_DEVICE_POWER_STATE PowerState; + } GenericInfo; + + InfoLength = sizeof(ULONG); + InfoPtr = &GenericInfo; + + switch (Oid) + { + case OID_GEN_SUPPORTED_LIST: + InfoPtr = (PVOID)&NvpSupportedOidList; + InfoLength = sizeof(NvpSupportedOidList); + break; + + case OID_GEN_HARDWARE_STATUS: + InfoLength = sizeof(NDIS_HARDWARE_STATUS); + GenericInfo.Status = NdisHardwareStatusReady; + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + { + InfoLength = sizeof(NDIS_MEDIUM); + GenericInfo.Medium = NdisMedium802_3; + break; + } + + case OID_GEN_CURRENT_LOOKAHEAD: + case OID_GEN_MAXIMUM_LOOKAHEAD: + { + GenericInfo.Ulong = Adapter->MaximumFrameSize - sizeof(ETH_HEADER); + break; + } + + case OID_GEN_MAXIMUM_FRAME_SIZE: + { + GenericInfo.Ulong = Adapter->MaximumFrameSize; + break; + } + + case OID_GEN_LINK_SPEED: + { + GenericInfo.Ulong = NvNetGetLinkSpeed(Adapter) * 10000; + break; + } + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + { + /* TODO: Change this later, once the driver can handle multipacket sends */ + GenericInfo.Ulong = Adapter->MaximumFrameSize; + break; + } + + case OID_GEN_RECEIVE_BUFFER_SPACE: + { + GenericInfo.Ulong = Adapter->MaximumFrameSize * NVNET_RECEIVE_DESCRIPTORS; + } + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + { + GenericInfo.Ulong = Adapter->MaximumFrameSize; + break; + } + + case OID_GEN_VENDOR_ID: + { + GenericInfo.Ulong = 0; + GenericInfo.Ulong |= (Adapter->PermanentMacAddress[0] << 16); + GenericInfo.Ulong |= (Adapter->PermanentMacAddress[1] << 8); + GenericInfo.Ulong |= (Adapter->PermanentMacAddress[2] & 0xFF); + break; + } + + case OID_GEN_DRIVER_VERSION: + { + InfoLength = sizeof(USHORT); + GenericInfo.Ushort = (NDIS_MINIPORT_MAJOR_VERSION << 8) | NDIS_MINIPORT_MINOR_VERSION; + break; + } + + case OID_GEN_VENDOR_DESCRIPTION: + { + static const CHAR VendorDesc[] = "nVidia nForce Ethernet Controller"; + InfoPtr = (PVOID)&VendorDesc; + InfoLength = sizeof(VendorDesc); + break; + } + + case OID_GEN_CURRENT_PACKET_FILTER: + { + GenericInfo.Ulong = Adapter->PacketFilter; + break; + } + + case OID_GEN_MAC_OPTIONS: + { + GenericInfo.Ulong = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_NO_LOOPBACK; + + if (Adapter->Flags & NV_PACKET_PRIORITY) + GenericInfo.Ulong |= NDIS_MAC_OPTION_8021P_PRIORITY; + if (Adapter->Flags & NV_VLAN_TAGGING) + GenericInfo.Ulong |= NDIS_MAC_OPTION_8021Q_VLAN; + break; + } + + case OID_GEN_MEDIA_CONNECT_STATUS: + { + GenericInfo.Ulong = Adapter->Connected ? NdisMediaStateConnected + : NdisMediaStateDisconnected; + break; + } + + case OID_GEN_MAXIMUM_SEND_PACKETS: + { + /* TODO: Multipacket sends */ + GenericInfo.Ulong = 1; + break; + } + + case OID_GEN_VENDOR_DRIVER_VERSION: + { + /* 1.0.0 */ + GenericInfo.Ulong = 0x100; + break; + } + + case OID_GEN_XMIT_OK: + case OID_GEN_RCV_OK: + case OID_GEN_XMIT_ERROR: + case OID_GEN_RCV_ERROR: + case OID_GEN_RCV_NO_BUFFER: + case OID_GEN_DIRECTED_FRAMES_RCV: + case OID_GEN_RCV_CRC_ERROR: + case OID_802_3_RCV_ERROR_ALIGNMENT: + case OID_802_3_XMIT_ONE_COLLISION: + case OID_802_3_XMIT_MORE_COLLISIONS: + case OID_802_3_XMIT_DEFERRED: + case OID_802_3_XMIT_MAX_COLLISIONS: + case OID_802_3_RCV_OVERRUN: + case OID_802_3_XMIT_UNDERRUN: + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + case OID_802_3_XMIT_TIMES_CRS_LOST: + case OID_802_3_XMIT_LATE_COLLISIONS: + { + if (Adapter->Features & DEV_HAS_STATISTICS_COUNTERS) + { + NvNetReadStatistics(Adapter); + NvNetQueryHwCounter(Adapter, Oid, &GenericInfo.Ulong64); + } + else + { + NvNetQuerySoftwareCounter(Adapter, Oid, &GenericInfo.Ulong64); + } + + *BytesNeeded = sizeof(ULONG64); + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesWritten = 0; + return NDIS_STATUS_BUFFER_TOO_SHORT; + } + if (InformationBufferLength >= sizeof(ULONG64)) + { + *BytesWritten = sizeof(ULONG64); + NdisMoveMemory(InformationBuffer, InfoPtr, sizeof(ULONG64)); + } + else + { + *BytesWritten = sizeof(ULONG); + NdisMoveMemory(InformationBuffer, InfoPtr, sizeof(ULONG)); + } + + return NDIS_STATUS_SUCCESS; + } + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + { + GenericInfo.Ulong = NVNET_TRANSMIT_BLOCKS - Adapter->Send.TcbSlots; + break; + } + + case OID_802_3_PERMANENT_ADDRESS: + { + InfoPtr = Adapter->PermanentMacAddress; + InfoLength = ETH_LENGTH_OF_ADDRESS; + break; + } + + case OID_802_3_CURRENT_ADDRESS: + { + InfoPtr = Adapter->CurrentMacAddress; + InfoLength = ETH_LENGTH_OF_ADDRESS; + break; + } + + case OID_802_3_MULTICAST_LIST: + { + InfoPtr = Adapter->MulticastList; + InfoLength = Adapter->MulticastListSize * ETH_LENGTH_OF_ADDRESS; + break; + } + + case OID_802_3_MAXIMUM_LIST_SIZE: + { + GenericInfo.Ulong = NVNET_MULTICAST_LIST_SIZE; + break; + } + + case OID_TCP_TASK_OFFLOAD: + { + return NvNetGetTcpTaskOffload(Adapter, + InformationBuffer, + InformationBufferLength, + BytesWritten, + BytesWritten); + } + + case OID_PNP_ENABLE_WAKE_UP: + { + GenericInfo.Ulong = NvNetGetWakeUp(Adapter); + break; + } + + case OID_PNP_CAPABILITIES: + { + InfoLength = sizeof(NDIS_PNP_CAPABILITIES); + + if (InformationBufferLength < InfoLength) + { + *BytesWritten = 0; + *BytesNeeded = InfoLength; + return NDIS_STATUS_BUFFER_TOO_SHORT; + } + + *BytesWritten = InfoLength; + *BytesNeeded = 0; + return NvNetFillPowerManagementCapabilities(Adapter, InformationBuffer); + } + + case OID_PNP_QUERY_POWER: + { + return NDIS_STATUS_SUCCESS; + } + + case OID_GEN_VLAN_ID: + { + /* TODO: Implement software VLAN support */ + if (!(Adapter->Flags & NV_VLAN_TAGGING)) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + GenericInfo.Ulong = Adapter->VlanId; + break; + } + + default: + Status = NDIS_STATUS_INVALID_OID; + break; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + if (InfoLength > InformationBufferLength) + { + *BytesWritten = 0; + *BytesNeeded = InfoLength; + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + else + { + NdisMoveMemory(InformationBuffer, InfoPtr, InfoLength); + *BytesWritten = InfoLength; + *BytesNeeded = 0; + } + } + else + { + *BytesWritten = 0; + *BytesNeeded = 0; + } + + return Status; +} + +NDIS_STATUS +NTAPI +MiniportSetInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesRead, + _Out_ PULONG BytesNeeded) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + ULONG GenericUlong; + + *BytesRead = 0; + *BytesNeeded = 0; + + switch (Oid) + { + case OID_802_3_MULTICAST_LIST: + { + if (InformationBufferLength % ETH_LENGTH_OF_ADDRESS) + { + *BytesNeeded = (InformationBufferLength / ETH_LENGTH_OF_ADDRESS) * + ETH_LENGTH_OF_ADDRESS; + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (InformationBufferLength > sizeof(Adapter->MulticastList)) + { + *BytesNeeded = sizeof(Adapter->MulticastList); + Status = NDIS_STATUS_MULTICAST_FULL; + break; + } + + *BytesRead = InformationBufferLength; + NdisMoveMemory(Adapter->MulticastList, InformationBuffer, InformationBufferLength); + + Adapter->MulticastListSize = InformationBufferLength / ETH_LENGTH_OF_ADDRESS; + + NvNetApplyPacketFilter(Adapter); + break; + } + + case OID_GEN_CURRENT_PACKET_FILTER: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + if (GenericUlong & ~NVNET_PACKET_FILTERS) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + /* Do not check to see if the filter is the same filter */ + Adapter->PacketFilter = GenericUlong; + + NvNetApplyPacketFilter(Adapter); + break; + } + + case OID_GEN_CURRENT_LOOKAHEAD: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + /* Nothing to do */ + *BytesRead = sizeof(ULONG); + break; + } + + case OID_GEN_VLAN_ID: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (!(Adapter->Flags & NV_VLAN_TAGGING)) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + if (GenericUlong > NVNET_MAXIMUM_VLAN_ID) + { + Status = NDIS_STATUS_FAILURE; + break; + } + + Adapter->VlanId = GenericUlong; + break; + } + + case OID_TCP_TASK_OFFLOAD: + { + if (InformationBufferLength < sizeof(NDIS_TASK_OFFLOAD_HEADER)) + { + *BytesNeeded = sizeof(NDIS_TASK_OFFLOAD_HEADER); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = InformationBufferLength; + + Status = NvNetSetTcpTaskOffload(Adapter, InformationBuffer, BytesRead); + break; + } + + case OID_PNP_ADD_WAKE_UP_PATTERN: + { + if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) + { + *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = sizeof(NDIS_PM_PACKET_PATTERN); + + Status = NvNetAddWakeUpPattern(Adapter, InformationBuffer); + break; + } + + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + { + if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) + { + *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = sizeof(NDIS_PM_PACKET_PATTERN); + + Status = NvNetRemoveWakeUpPattern(Adapter, InformationBuffer); + break; + } + + case OID_PNP_ENABLE_WAKE_UP: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + NvNetEnableWakeUp(Adapter, GenericUlong); + break; + } + + case OID_PNP_SET_POWER: + { + if (InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE)) + { + *BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + if (GenericUlong < NdisDeviceStateD0 || GenericUlong > NdisDeviceStateD3) + { + Status = NDIS_STATUS_INVALID_DATA; + break; + } + + Status = NvNetSetPower(Adapter, GenericUlong); + break; + } + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + return Status; +} diff --git a/drivers/network/dd/nvnet/send.c b/drivers/network/dd/nvnet/send.c new file mode 100644 index 00000000000..60d8afb2cd6 --- /dev/null +++ b/drivers/network/dd/nvnet/send.c @@ -0,0 +1,524 @@ +/* + * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Packet sending + * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "nvnet.h" + +#define NDEBUG +#include "debug.h" + +/* FUNCTIONS ******************************************************************/ + +VOID +NvNetTransmitPacket32( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNVNET_TCB Tcb, + _In_ PSCATTER_GATHER_LIST SgList) +{ + NVNET_TBD Tbd, LastTbd; + ULONG i, Flags; + ULONG Slots; + + Flags = 0; + Slots = 0; + Tbd = Adapter->Send.CurrentTbd; + + for (i = 0; i < SgList->NumberOfElements; ++i) + { + ULONG Address = NdisGetPhysicalAddressLow(SgList->Elements[i].Address); + ULONG Length = SgList->Elements[i].Length; + + if (Length > NV_MAXIMUM_SG_SIZE) + { + ULONG ImplicitEntries = NV_IMPLICIT_ENTRIES(Length); + + do + { + ++Slots; + + Tbd.x32->Address = Address; + Tbd.x32->FlagsLength = Flags | (NV_MAXIMUM_SG_SIZE - 1); + LastTbd = Tbd; + Tbd = NV_NEXT_TBD_32(Adapter, Tbd); + + Flags = NV_TX_VALID; + + Length -= NV_MAXIMUM_SG_SIZE; + Address += NV_MAXIMUM_SG_SIZE; + + --ImplicitEntries; + } + while (ImplicitEntries); + } + + ++Slots; + + Tbd.x32->Address = Address; + Tbd.x32->FlagsLength = Flags | (Length - 1); + LastTbd = Tbd; + Tbd = NV_NEXT_TBD_32(Adapter, Tbd); + + Flags = NV_TX_VALID; + } + + Tcb->Slots = Slots; + Tcb->Tbd = LastTbd; + + if (Adapter->Features & DEV_HAS_LARGEDESC) + { + LastTbd.x32->FlagsLength |= NV_TX2_LASTPACKET; + } + else + { + LastTbd.x32->FlagsLength |= NV_TX_LASTPACKET; + } + + if (Tcb->Flags & NV_TCB_LARGE_SEND) + { + Flags |= (Tcb->Mss << NV_TX2_TSO_SHIFT) | NV_TX2_TSO; + } + else + { + if (Tcb->Flags & NV_TCB_CHECKSUM_IP) + { + Flags |= NV_TX2_CHECKSUM_L3; + } + if (Tcb->Flags & (NV_TCB_CHECKSUM_TCP | NV_TCB_CHECKSUM_UDP)) + { + Flags |= NV_TX2_CHECKSUM_L4; + } + } + + Adapter->Send.CurrentTbd.x32->FlagsLength |= Flags; + Adapter->Send.CurrentTbd = Tbd; + + NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl | NVREG_TXRXCTL_KICK); +} + +VOID +NvNetTransmitPacket64( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNVNET_TCB Tcb, + _In_ PSCATTER_GATHER_LIST SgList) +{ + NVNET_TBD Tbd, LastTbd; + ULONG i, Flags; + ULONG Slots; + + Flags = 0; + Slots = 0; + Tbd = Adapter->Send.CurrentTbd; + + for (i = 0; i < SgList->NumberOfElements; ++i) + { + ULONG64 Address = SgList->Elements[i].Address.QuadPart; + ULONG Length = SgList->Elements[i].Length; + + if (Length > NV_MAXIMUM_SG_SIZE) + { + ULONG ImplicitEntries = NV_IMPLICIT_ENTRIES(Length); + + do + { + ++Slots; + + Tbd.x64->AddressLow = (ULONG)Address; + Tbd.x64->AddressHigh = Address >> 32; + Tbd.x64->VlanTag = 0; + Tbd.x64->FlagsLength = Flags | (NV_MAXIMUM_SG_SIZE - 1); + LastTbd = Tbd; + Tbd = NV_NEXT_TBD_64(Adapter, Tbd); + + Flags = NV_TX2_VALID; + + Length -= NV_MAXIMUM_SG_SIZE; + Address += NV_MAXIMUM_SG_SIZE; + + --ImplicitEntries; + } + while (ImplicitEntries); + } + + ++Slots; + + Tbd.x64->AddressLow = (ULONG)Address; + Tbd.x64->AddressHigh = Address >> 32; + Tbd.x64->VlanTag = 0; + Tbd.x64->FlagsLength = Flags | (Length - 1); + LastTbd = Tbd; + Tbd = NV_NEXT_TBD_64(Adapter, Tbd); + + Flags = NV_TX2_VALID; + } + + Tcb->Slots = Slots; + Tcb->Tbd = LastTbd; + + LastTbd.x64->FlagsLength |= NV_TX2_LASTPACKET; + + if (Adapter->Flags & NV_SEND_ERRATA_PRESENT) + { + if (Adapter->Send.PacketsCount == NV_TX_LIMIT_COUNT) + { + Tcb->DeferredTbd = Adapter->Send.CurrentTbd; + + if (!Adapter->Send.DeferredTcb) + { + Adapter->Send.DeferredTcb = Tcb; + } + + Flags = 0; + } + else + { + ++Adapter->Send.PacketsCount; + } + } + + if (Tcb->Flags & NV_TCB_LARGE_SEND) + { + Flags |= (Tcb->Mss << NV_TX2_TSO_SHIFT) | NV_TX2_TSO; + } + else + { + if (Tcb->Flags & NV_TCB_CHECKSUM_IP) + { + Flags |= NV_TX2_CHECKSUM_L3; + } + if (Tcb->Flags & (NV_TCB_CHECKSUM_TCP | NV_TCB_CHECKSUM_UDP)) + { + Flags |= NV_TX2_CHECKSUM_L4; + } + } + + // Adapter->Send.CurrentTbd.x64->VlanTag = NV_TX3_VLAN_TAG_PRESENT; TODO + Adapter->Send.CurrentTbd.x64->FlagsLength |= Flags; + Adapter->Send.CurrentTbd = Tbd; + + NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl | NVREG_TXRXCTL_KICK); +} + +static +DECLSPEC_NOINLINE +ULONG +NvNetQueryTcpIpHeaders( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PACKET Packet) +{ + PNDIS_BUFFER CurrentBuffer; + PVOID Address; + UINT CurrentLength; + UINT PacketLength; + PIPv4_HEADER IpHeader; + PTCPv4_HEADER TcpHeader; + ULONG BytesCopied = 0; + UCHAR Buffer[136]; + + NdisGetFirstBufferFromPacketSafe(Packet, + &CurrentBuffer, + &Address, + &CurrentLength, + &PacketLength, + HighPagePriority); + if (!Address) + return 0; + + while (TRUE) + { + CurrentLength = min(CurrentLength, sizeof(Buffer) - BytesCopied); + + NdisMoveMemory(&Buffer[BytesCopied], Address, CurrentLength); + BytesCopied += CurrentLength; + + if (BytesCopied >= sizeof(Buffer)) + break; + + NdisGetNextBuffer(CurrentBuffer, &CurrentBuffer); + + if (!CurrentBuffer) + return 0; + + NdisQueryBufferSafe(CurrentBuffer, + &Address, + &CurrentLength, + HighPagePriority); + } + + IpHeader = (PIPv4_HEADER)&Buffer[Adapter->IpHeaderOffset]; + TcpHeader = (PTCPv4_HEADER)((PUCHAR)IpHeader + IP_HEADER_LENGTH(IpHeader)); + + return IP_HEADER_LENGTH(IpHeader) + TCP_HEADER_LENGTH(TcpHeader); +} + +static +BOOLEAN +NvNetCopyPacket( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PACKET Packet, + _In_ PNVNET_TX_BUFFER Buffer) +{ + PNDIS_BUFFER CurrentBuffer; + PVOID Address; + UINT CurrentLength; + UINT PacketLength; + PUCHAR Destination; + + NdisGetFirstBufferFromPacketSafe(Packet, + &CurrentBuffer, + &Address, + &CurrentLength, + &PacketLength, + HighPagePriority); + if (!Address) + return FALSE; + + Destination = Buffer->VirtualAddress; + + while (TRUE) + { + NdisMoveMemory(Destination, Address, CurrentLength); + Destination += CurrentLength; + + NdisGetNextBuffer(CurrentBuffer, &CurrentBuffer); + + if (!CurrentBuffer) + break; + + NdisQueryBufferSafe(CurrentBuffer, + &Address, + &CurrentLength, + HighPagePriority); + if (!Address) + return FALSE; + } + + return TRUE; +} + +static +NDIS_STATUS +NvNetSendPacketLargeSend( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PACKET Packet, + _In_ ULONG TotalLength) +{ + PSCATTER_GATHER_LIST SgList; + ULONG Mss, Length; + PNVNET_TCB Tcb; + + if (!Adapter->Send.TcbSlots) + { + return NDIS_STATUS_RESOURCES; + } + + SgList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo); + + /* Make sure we have room to setup all fragments */ + C_ASSERT(NVNET_TRANSMIT_DESCRIPTORS > ((NVNET_MAXIMUM_LSO_FRAME_SIZE / PAGE_SIZE) + 3)); + ASSERT(SgList->NumberOfElements + + (NVNET_MAXIMUM_LSO_FRAME_SIZE / (NV_MAXIMUM_SG_SIZE + 1)) <= + NVNET_TRANSMIT_DESCRIPTORS); + + if (SgList->NumberOfElements + + (NVNET_MAXIMUM_LSO_FRAME_SIZE / (NV_MAXIMUM_SG_SIZE + 1)) < Adapter->Send.TbdSlots) + { + return NDIS_STATUS_RESOURCES; + } + + Length = NvNetQueryTcpIpHeaders(Adapter, Packet); + if (!Length) + { + return NDIS_STATUS_RESOURCES; + } + + NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo) = + UlongToPtr(TotalLength - Adapter->IpHeaderOffset - Length); + + --Adapter->Send.TcbSlots; + + Mss = PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo)); + + Tcb = Adapter->Send.CurrentTcb; + Tcb->Mss = Mss; + Tcb->Packet = Packet; + Tcb->Flags = NV_TCB_LARGE_SEND; + + Adapter->TransmitPacket(Adapter, Tcb, SgList); + + ASSERT(Adapter->Send.TbdSlots >= Tcb->Slots); + Adapter->Send.TbdSlots -= Tcb->Slots; + + Adapter->Send.CurrentTcb = NV_NEXT_TCB(Adapter, Tcb); + + return NDIS_STATUS_SUCCESS; +} + +static +ULONG +NvNetGetChecksumInfo( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PACKET Packet) +{ + ULONG Flags; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO ChecksumInfo; + + if (NDIS_GET_PACKET_PROTOCOL_TYPE(Packet) != NDIS_PROTOCOL_ID_TCP_IP) + return 0; + + ChecksumInfo.Value = PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, + TcpIpChecksumPacketInfo)); + + Flags = 0; + if (ChecksumInfo.Transmit.NdisPacketChecksumV4) + { + if (ChecksumInfo.Transmit.NdisPacketTcpChecksum && Adapter->Offload.SendTcpChecksum) + { + Flags |= NV_TCB_CHECKSUM_TCP; + } + if (ChecksumInfo.Transmit.NdisPacketUdpChecksum && Adapter->Offload.SendUdpChecksum) + { + Flags |= NV_TCB_CHECKSUM_UDP; + } + if (ChecksumInfo.Transmit.NdisPacketIpChecksum && Adapter->Offload.SendIpChecksum) + { + Flags |= NV_TCB_CHECKSUM_IP; + } + } + + return Flags; +} + +static +NDIS_STATUS +NvNetSendPacket( + _In_ PNVNET_ADAPTER Adapter, + _In_ PNDIS_PACKET Packet, + _In_ ULONG TotalLength) +{ + PSCATTER_GATHER_LIST SgList; + SCATTER_GATHER_LIST LocalSgList; + PNVNET_TCB Tcb; + ULONG Flags; + + ASSERT(TotalLength <= Adapter->MaximumFrameSize); + + if (!Adapter->Send.TcbSlots) + { + return NDIS_STATUS_RESOURCES; + } + + Flags = NvNetGetChecksumInfo(Adapter, Packet); + + SgList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo); + + if (SgList->NumberOfElements > NVNET_FRAGMENTATION_THRESHOLD) + { + if (!Adapter->Send.TbdSlots || !Adapter->Send.BufferList.Next) + { + return NDIS_STATUS_RESOURCES; + } + else + { + PNVNET_TX_BUFFER CoalesceBuffer; + BOOLEAN Success; + + --Adapter->Send.TcbSlots; + + CoalesceBuffer = (PNVNET_TX_BUFFER)PopEntryList(&Adapter->Send.BufferList); + + NdisDprReleaseSpinLock(&Adapter->Send.Lock); + + Success = NvNetCopyPacket(Adapter, Packet, CoalesceBuffer); + + NdisDprAcquireSpinLock(&Adapter->Send.Lock); + + if (!Success || !Adapter->Send.TbdSlots || !(Adapter->Flags & NV_ACTIVE)) + { + PushEntryList(&Adapter->Send.BufferList, &CoalesceBuffer->Link); + + ++Adapter->Send.TcbSlots; + + return NDIS_STATUS_RESOURCES; + } + + Flags |= NV_TCB_COALESCE; + + LocalSgList.NumberOfElements = 1; + LocalSgList.Elements[0].Address = CoalesceBuffer->PhysicalAddress; + LocalSgList.Elements[0].Length = TotalLength; + SgList = &LocalSgList; + + Tcb = Adapter->Send.CurrentTcb; + Tcb->Buffer = CoalesceBuffer; + } + } + else + { + if (SgList->NumberOfElements + + (NVNET_MAXIMUM_FRAME_SIZE_JUMBO / (NV_MAXIMUM_SG_SIZE + 1)) > Adapter->Send.TbdSlots) + { + return NDIS_STATUS_RESOURCES; + } + + --Adapter->Send.TcbSlots; + + Tcb = Adapter->Send.CurrentTcb; + } + + Tcb->Packet = Packet; + Tcb->Flags = Flags; + + Adapter->TransmitPacket(Adapter, Tcb, SgList); + + ASSERT(Adapter->Send.TbdSlots >= Tcb->Slots); + Adapter->Send.TbdSlots -= Tcb->Slots; + + Adapter->Send.CurrentTcb = NV_NEXT_TCB(Adapter, Tcb); + + return NDIS_STATUS_PENDING; +} + +/* FIXME: Use the proper send function (MiniportSendPackets) */ +NDIS_STATUS +NTAPI +MiniportSend( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_PACKET Packet, + _In_ UINT Flags) +{ + PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; + UINT TotalLength; + NDIS_STATUS Status; + + NDIS_DbgPrint(MIN_TRACE, ("()\n")); + + NdisQueryPacketLength(Packet, &TotalLength); + + NdisDprAcquireSpinLock(&Adapter->Send.Lock); + + if (!(Adapter->Flags & NV_ACTIVE)) + { + NdisDprReleaseSpinLock(&Adapter->Send.Lock); + + return NDIS_STATUS_FAILURE; + } + + if (Adapter->Flags & NV_SEND_LARGE_SEND && + PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo))) + { + Status = NvNetSendPacketLargeSend(Adapter, Packet, TotalLength); + } + else + { + Status = NvNetSendPacket(Adapter, Packet, TotalLength); + } + + NdisDprReleaseSpinLock(&Adapter->Send.Lock); + + return Status; +}