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