mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
b79fbe2333
The driver supports all nVidia chipset models from 2001 until 2010, starting from nForce. All NICs are compatible with x86 and amd64 devices only. Tested by Daniel Reimer on OG Xbox and by me on MCP board. CORE-15872 CORE-16216
524 lines
13 KiB
C
524 lines
13 KiB
C
/*
|
|
* PROJECT: ReactOS nVidia nForce Ethernet Controller Driver
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Packet sending
|
|
* COPYRIGHT: Copyright 2021-2022 Dmitry Borisov <di.sean@protonmail.com>
|
|
*/
|
|
|
|
/* 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;
|
|
}
|