mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
59d8a77df6
These adapters were common in DEC Alpha boxes and they are really rare nowadays. The 21140 chip is emulated in Connectix / Microsoft Virtual PC and Hyper-V Gen 1 VM. This is an experimental driver, not yet tested on real hardware. CORE-8724
313 lines
7.7 KiB
C
313 lines
7.7 KiB
C
/*
|
|
* PROJECT: ReactOS DC21x4 Driver
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Packet sending
|
|
* COPYRIGHT: Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "dc21x4.h"
|
|
|
|
#include <debug.h>
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
static
|
|
VOID
|
|
DcTransmitPacket(
|
|
_In_ PDC21X4_ADAPTER Adapter,
|
|
_In_ PDC_TCB Tcb,
|
|
_In_ PSCATTER_GATHER_LIST SgList)
|
|
{
|
|
PDC_TBD Tbd, NextTbd, FirstTbd, LastTbd;
|
|
ULONG i, TbdStatus;
|
|
|
|
TbdStatus = 0;
|
|
Tbd = Adapter->CurrentTbd;
|
|
|
|
for (i = 0; i < SgList->NumberOfElements; ++i)
|
|
{
|
|
ULONG Address, Length;
|
|
|
|
LastTbd = Tbd;
|
|
|
|
/* Not owned by NIC */
|
|
ASSERT(!(((i % 2) == 0) && (Tbd->Status & DC_TBD_STATUS_OWNED)));
|
|
|
|
Tbd->Status = TbdStatus;
|
|
|
|
/* 32-bit DMA */
|
|
ASSERT(SgList->Elements[i].Address.HighPart == 0);
|
|
|
|
Address = SgList->Elements[i].Address.LowPart;
|
|
Length = SgList->Elements[i].Length;
|
|
|
|
/* Two data buffers per descriptor */
|
|
if ((i % 2) == 0)
|
|
{
|
|
Tbd->Control &= DC_TBD_CONTROL_END_OF_RING;
|
|
|
|
Tbd->Control |= Length;
|
|
Tbd->Address1 = Address;
|
|
|
|
NextTbd = DC_NEXT_TBD(Adapter, Tbd);
|
|
}
|
|
else
|
|
{
|
|
Tbd->Control |= Length << DC_TBD_CONTROL_LENGTH_2_SHIFT;
|
|
Tbd->Address2 = Address;
|
|
|
|
Tbd = NextTbd;
|
|
TbdStatus = DC_TBD_STATUS_OWNED;
|
|
}
|
|
}
|
|
|
|
/* Enable IRQ on last element */
|
|
LastTbd->Control |= DC_TBD_CONTROL_LAST_FRAGMENT | DC_TBD_CONTROL_REQUEST_INTERRUPT;
|
|
|
|
Tcb->Tbd = LastTbd;
|
|
|
|
FirstTbd = Adapter->CurrentTbd;
|
|
Adapter->CurrentTbd = NextTbd;
|
|
|
|
/* Not owned by NIC */
|
|
ASSERT(!(FirstTbd->Status & DC_TBD_STATUS_OWNED));
|
|
|
|
FirstTbd->Control |= DC_TBD_CONTROL_FIRST_FRAGMENT;
|
|
DC_WRITE_BARRIER();
|
|
FirstTbd->Status = DC_TBD_STATUS_OWNED;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
DcCopyPacket(
|
|
_In_ PDC21X4_ADAPTER Adapter,
|
|
_In_ PNDIS_PACKET Packet,
|
|
_In_ PDC_COALESCE_BUFFER Buffer)
|
|
{
|
|
PNDIS_BUFFER CurrentBuffer;
|
|
PVOID Address;
|
|
UINT CurrentLength, 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
|
|
DcSendPacket(
|
|
_In_ NDIS_HANDLE MiniportAdapterContext,
|
|
_In_ PNDIS_PACKET Packet)
|
|
{
|
|
PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
|
|
PSCATTER_GATHER_LIST SgList;
|
|
PDC_TCB Tcb;
|
|
ULONG SlotsUsed;
|
|
|
|
if (!Adapter->TcbSlots)
|
|
return NDIS_STATUS_RESOURCES;
|
|
|
|
SgList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo);
|
|
|
|
if (SgList->NumberOfElements > DC_FRAGMENTATION_THRESHOLD)
|
|
{
|
|
PDC_COALESCE_BUFFER CoalesceBuffer;
|
|
UINT PacketLength;
|
|
|
|
if (!Adapter->TbdSlots || !Adapter->SendBufferList.Next)
|
|
{
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
NdisQueryPacketLength(Packet, &PacketLength);
|
|
|
|
CoalesceBuffer = (PDC_COALESCE_BUFFER)PopEntryList(&Adapter->SendBufferList);
|
|
|
|
if (!DcCopyPacket(Adapter, Packet, CoalesceBuffer))
|
|
{
|
|
PushEntryList(&Adapter->SendBufferList, &CoalesceBuffer->ListEntry);
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
SgList = &Adapter->LocalSgList;
|
|
SgList->Elements[0].Address.LowPart = CoalesceBuffer->PhysicalAddress;
|
|
SgList->Elements[0].Length = PacketLength;
|
|
SgList->NumberOfElements = 1;
|
|
SlotsUsed = 1;
|
|
|
|
Tcb = Adapter->CurrentTcb;
|
|
Tcb->SlotsUsed = 1;
|
|
Tcb->Buffer = CoalesceBuffer;
|
|
}
|
|
else
|
|
{
|
|
/* We use two data buffers per descriptor */
|
|
SlotsUsed = (SgList->NumberOfElements + 1) / 2;
|
|
|
|
if (SlotsUsed > Adapter->TbdSlots)
|
|
return NDIS_STATUS_RESOURCES;
|
|
|
|
Tcb = Adapter->CurrentTcb;
|
|
Tcb->SlotsUsed = SlotsUsed;
|
|
Tcb->Buffer = NULL;
|
|
}
|
|
|
|
--Adapter->TcbSlots;
|
|
|
|
Adapter->CurrentTcb = DC_NEXT_TCB(Adapter, Tcb);
|
|
|
|
Tcb->Packet = Packet;
|
|
|
|
ASSERT(Adapter->TbdSlots >= Tcb->SlotsUsed);
|
|
Adapter->TbdSlots -= SlotsUsed;
|
|
|
|
DcTransmitPacket(Adapter, Tcb, SgList);
|
|
|
|
DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL);
|
|
|
|
return NDIS_STATUS_PENDING;
|
|
}
|
|
|
|
VOID
|
|
DcProcessPendingPackets(
|
|
_In_ PDC21X4_ADAPTER Adapter)
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
NDIS_STATUS Status;
|
|
PNDIS_PACKET Packet;
|
|
|
|
ASSERT(!IsListEmpty(&Adapter->SendQueueList));
|
|
|
|
do
|
|
{
|
|
Entry = RemoveHeadList(&Adapter->SendQueueList);
|
|
|
|
Packet = DC_PACKET_FROM_LIST_ENTRY(Entry);
|
|
|
|
Status = DcSendPacket(Adapter, Packet);
|
|
if (Status == NDIS_STATUS_RESOURCES)
|
|
{
|
|
InsertHeadList(&Adapter->SendQueueList, DC_LIST_ENTRY_FROM_PACKET(Packet));
|
|
break;
|
|
}
|
|
}
|
|
while (!IsListEmpty(&Adapter->SendQueueList));
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
DcSendPackets(
|
|
_In_ NDIS_HANDLE MiniportAdapterContext,
|
|
_In_ PPNDIS_PACKET PacketArray,
|
|
_In_ UINT NumberOfPackets)
|
|
{
|
|
PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
|
|
NDIS_STATUS Status;
|
|
ULONG i;
|
|
|
|
NdisAcquireSpinLock(&Adapter->SendLock);
|
|
|
|
if (!(Adapter->Flags & DC_ACTIVE))
|
|
{
|
|
NdisReleaseSpinLock(&Adapter->SendLock);
|
|
|
|
for (i = 0; i < NumberOfPackets; ++i)
|
|
{
|
|
NdisMSendComplete(Adapter->AdapterHandle,
|
|
PacketArray[i],
|
|
NDIS_STATUS_NOT_ACCEPTED);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
TRACE("Send packets %u\n", NumberOfPackets);
|
|
|
|
for (i = 0; i < NumberOfPackets; ++i)
|
|
{
|
|
PNDIS_PACKET Packet = PacketArray[i];
|
|
|
|
Status = DcSendPacket(Adapter, Packet);
|
|
if (Status == NDIS_STATUS_RESOURCES)
|
|
{
|
|
InsertTailList(&Adapter->SendQueueList, DC_LIST_ENTRY_FROM_PACKET(Packet));
|
|
}
|
|
}
|
|
|
|
NdisReleaseSpinLock(&Adapter->SendLock);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
DcCancelSendPackets(
|
|
_In_ NDIS_HANDLE MiniportAdapterContext,
|
|
_In_ PVOID CancelId)
|
|
{
|
|
PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
|
|
LIST_ENTRY DoneList;
|
|
PLIST_ENTRY Entry, NextEntry;
|
|
|
|
TRACE("Called\n");
|
|
|
|
InitializeListHead(&DoneList);
|
|
|
|
NdisAcquireSpinLock(&Adapter->SendLock);
|
|
|
|
NextEntry = Adapter->SendQueueList.Flink;
|
|
while (NextEntry != &Adapter->SendQueueList)
|
|
{
|
|
PNDIS_PACKET Packet;
|
|
|
|
Entry = NextEntry;
|
|
NextEntry = NextEntry->Flink;
|
|
|
|
Packet = DC_PACKET_FROM_LIST_ENTRY(Entry);
|
|
|
|
if (NDIS_GET_PACKET_CANCEL_ID(Packet) == CancelId)
|
|
{
|
|
RemoveEntryList(DC_LIST_ENTRY_FROM_PACKET(Packet));
|
|
|
|
InsertTailList(&DoneList, DC_LIST_ENTRY_FROM_PACKET(Packet));
|
|
}
|
|
}
|
|
|
|
NdisReleaseSpinLock(&Adapter->SendLock);
|
|
|
|
while (!IsListEmpty(&DoneList))
|
|
{
|
|
Entry = RemoveHeadList(&DoneList);
|
|
|
|
NdisMSendComplete(Adapter->AdapterHandle,
|
|
DC_PACKET_FROM_LIST_ENTRY(Entry),
|
|
NDIS_STATUS_REQUEST_ABORTED);
|
|
}
|
|
}
|