mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
525 lines
13 KiB
C
525 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;
|
||
|
}
|