windows-nt/Source/XPSP1/NT/net/ndis/sys/dma.c
2020-09-26 16:20:57 +08:00

756 lines
22 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
dma.c
Abstract:
Author:
Jameel Hyder (jameelh) 02-Apr-1998
Environment:
Kernel mode, FSD
Revision History:
02-Apr-1998 JameelH Initial version
--*/
#include <precomp.h>
#pragma hdrstop
//
// Define the module number for debug code.
//
#define MODULE_NUMBER MODULE_DMA
NDIS_STATUS
NdisMInitializeScatterGatherDma(
IN NDIS_HANDLE MiniportAdapterHandle,
IN BOOLEAN Dma64BitAddresses,
IN ULONG MaximumPhysicalMapping
)
/*++
Routine Description:
Allocates adapter channel for bus mastering devices.
Arguments:
Return Value:
None.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
DEVICE_DESCRIPTION DeviceDescription;
ULONG MapRegisters, SGMapRegsisters;
NDIS_STATUS Status = NDIS_STATUS_NOT_SUPPORTED;
NTSTATUS NtStatus;
ULONG ScatterGatherListSize;
BOOLEAN DereferenceDmaAdapter = FALSE;
BOOLEAN FreeSGListLookasideList = FALSE;
DBGPRINT_RAW(DBG_COMP_CONFIG, DBG_LEVEL_INFO,
("==>NdisMInitializeScatterGatherDma: Miniport %lx, Dma64BitAddresses %lx, MaximumPhysicalMapping 0x%lx\n",
Miniport, Dma64BitAddresses, MaximumPhysicalMapping));
do
{
if (!MINIPORT_TEST_FLAGS(Miniport, fMINIPORT_IS_NDIS_5 | fMINIPORT_BUS_MASTER))
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
if (MINIPORT_VERIFY_TEST_FLAG(Miniport, fMINIPORT_VERIFY_FAIL_INIT_SG))
{
Status = NDIS_STATUS_RESOURCES;
break;
}
NdisZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = TRUE;
DeviceDescription.BusNumber = Miniport->BusNumber;
DeviceDescription.DmaChannel = 0;
DeviceDescription.InterfaceType = Miniport->AdapterType;
if (Dma64BitAddresses)
{
DeviceDescription.Dma32BitAddresses = FALSE;
DeviceDescription.Dma64BitAddresses = TRUE;
MINIPORT_SET_FLAG(Miniport, fMINIPORT_64BITS_DMA);
}
else
{
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.Dma64BitAddresses = FALSE;
}
if (((MaximumPhysicalMapping * 2 - 2) / PAGE_SIZE) + 2 < Miniport->SGMapRegistersNeeded)
{
DeviceDescription.MaximumLength = (Miniport->SGMapRegistersNeeded - 1) << PAGE_SHIFT;
}
else
{
DeviceDescription.MaximumLength = MaximumPhysicalMapping*2;
}
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION2;
//
// Get the adapter object.
//
Miniport->SystemAdapterObject = IoGetDmaAdapter(Miniport->PhysicalDeviceObject,
&DeviceDescription,
&MapRegisters);
if (Miniport->SystemAdapterObject == NULL)
{
NdisWriteErrorLogEntry((NDIS_HANDLE)Miniport,
NDIS_ERROR_CODE_OUT_OF_RESOURCES,
1,
0xFFFFFFFF);
DBGPRINT_RAW(DBG_COMP_CONFIG, DBG_LEVEL_ERR,
("NdisMInitializeScatterGatherDma: Miniport %lx, IoGetDmaAdapter failed\n", Miniport));
Status = NDIS_STATUS_RESOURCES;
break;
}
DBGPRINT_RAW(DBG_COMP_CONFIG, DBG_LEVEL_INFO,
("NdisMInitializeScatterGatherDma: Miniport %lx, MapRegisters 0x%lx\n", Miniport, MapRegisters));
InterlockedIncrement(&Miniport->DmaAdapterRefCount);
DereferenceDmaAdapter = TRUE;
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_DESERIALIZE))
{
Miniport->SendCompleteHandler = ndisMSendCompleteSG;
}
Miniport->SGListLookasideList = (PNPAGED_LOOKASIDE_LIST)ALLOC_FROM_POOL(sizeof(NPAGED_LOOKASIDE_LIST), NDIS_TAG_DMA);
if (Miniport->SGListLookasideList == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
FreeSGListLookasideList = TRUE;
NtStatus = Miniport->SystemAdapterObject->DmaOperations->CalculateScatterGatherList(
Miniport->SystemAdapterObject,
NULL,
0,
MapRegisters * PAGE_SIZE,
&ScatterGatherListSize,
&SGMapRegsisters);
ASSERT(NT_SUCCESS(NtStatus));
ASSERT(SGMapRegsisters == MapRegisters);
if (!NT_SUCCESS(NtStatus))
{
Status = NDIS_STATUS_RESOURCES;
break;
}
Miniport->ScatterGatherListSize = ScatterGatherListSize;
ExInitializeNPagedLookasideList(Miniport->SGListLookasideList,
NULL,
NULL,
0,
ScatterGatherListSize,
NDIS_TAG_DMA,
0);
Status = NDIS_STATUS_SUCCESS;
MINIPORT_SET_FLAG(Miniport, fMINIPORT_SG_LIST);
Miniport->InfoFlags |= NDIS_MINIPORT_SG_LIST;
DereferenceDmaAdapter = FALSE;
FreeSGListLookasideList = FALSE;
}while (FALSE);
if (DereferenceDmaAdapter)
{
ndisDereferenceDmaAdapter(Miniport);
}
if (FreeSGListLookasideList && Miniport->SGListLookasideList)
{
ExDeleteNPagedLookasideList(Miniport->SGListLookasideList);
FREE_POOL(Miniport->SGListLookasideList);
Miniport->SGListLookasideList = NULL;
}
DBGPRINT_RAW(DBG_COMP_CONFIG, DBG_LEVEL_INFO,
("<==NdisMInitializeScatterGatherDma: Miniport %lx, Status %lx\n", Miniport, Status));
return(Status);
}
VOID
FASTCALL
ndisMAllocSGList(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PNDIS_BUFFER Buffer;
PVOID pBufferVa;
ULONG PacketLength;
PNDIS_BUFFER pNdisBuffer = NULL;
PVOID SGListBuffer;
KIRQL OldIrql;
NdisQueryPacket(Packet, NULL, NULL, &Buffer, &PacketLength);
pBufferVa = MmGetMdlVirtualAddress(Buffer);
SGListBuffer = ExAllocateFromNPagedLookasideList(Miniport->SGListLookasideList);
//
// Callers of GetScatterGatherList must be at dispatch.
//
RAISE_IRQL_TO_DISPATCH(&OldIrql);
if (SGListBuffer)
{
Packet->Private.Flags = NdisGetPacketFlags(Packet) | NDIS_FLAGS_USES_SG_BUFFER_LIST;
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = SGListBuffer;
Status = Miniport->SystemAdapterObject->DmaOperations->BuildScatterGatherList(
Miniport->SystemAdapterObject,
Miniport->DeviceObject,
Buffer,
pBufferVa,
PacketLength,
ndisMProcessSGList,
Packet,
TRUE,
SGListBuffer,
Miniport->ScatterGatherListSize);
if (!NT_SUCCESS(Status))
{
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = NULL;
NdisClearPacketFlags(Packet, NDIS_FLAGS_USES_SG_BUFFER_LIST);
ExFreeToNPagedLookasideList(Miniport->SGListLookasideList, SGListBuffer);
}
}
else
{
Status = NDIS_STATUS_RESOURCES;
}
if (!NT_SUCCESS(Status))
{
Status = Miniport->SystemAdapterObject->DmaOperations->GetScatterGatherList(
Miniport->SystemAdapterObject,
Miniport->DeviceObject,
Buffer,
pBufferVa,
PacketLength,
ndisMProcessSGList,
Packet,
TRUE);
}
LOWER_IRQL(OldIrql, DISPATCH_LEVEL);
if (!NT_SUCCESS(Status))
{
PCHAR NewBuffer;
UINT BytesCopied;
do
{
//
// Allocate a buffer for the packet.
//
NewBuffer = (PUCHAR)ALLOC_FROM_POOL(PacketLength, NDIS_TAG_DOUBLE_BUFFER_PKT);
if (NULL == NewBuffer)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Allocate an MDL for the buffer
//
NdisAllocateBuffer(&Status, &pNdisBuffer, NULL, (PVOID)NewBuffer, PacketLength);
if (NDIS_STATUS_SUCCESS != Status)
{
break;
}
ndisMCopyFromPacketToBuffer(Packet, // Packet to copy from.
0, // Offset from beginning of packet.
PacketLength, // Number of bytes to copy.
NewBuffer, // The destination buffer.
&BytesCopied); // The number of bytes copied.
Packet->Private.Flags = NdisGetPacketFlags(Packet) | NDIS_FLAGS_DOUBLE_BUFFERED;
pBufferVa = MmGetMdlVirtualAddress(pNdisBuffer);
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = pNdisBuffer;
RAISE_IRQL_TO_DISPATCH(&OldIrql);
Status = Miniport->SystemAdapterObject->DmaOperations->GetScatterGatherList(
Miniport->SystemAdapterObject,
Miniport->DeviceObject,
pNdisBuffer,
pBufferVa,
PacketLength,
ndisMProcessSGList,
Packet,
TRUE);
LOWER_IRQL(OldIrql, DISPATCH_LEVEL);
}while (FALSE);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
("ndisMAllocSGList: GetScatterGatherList failed %lx\n", Status));
if (pNdisBuffer)
{
NdisFreeBuffer(pNdisBuffer);
}
if (NewBuffer)
{
FREE_POOL(NewBuffer);
}
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo) = NULL;
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = NULL;
NdisClearPacketFlags(Packet, NDIS_FLAGS_DOUBLE_BUFFERED);
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO))
{
PNDIS_STACK_RESERVED NSR;
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR);
NdisMCoSendComplete(NDIS_STATUS_FAILURE, NSR->VcPtr, Packet);
}
else
{
ndisMSendCompleteX(Miniport, Packet, NDIS_STATUS_FAILURE);
}
}
}
}
VOID
FASTCALL
ndisMFreeSGList(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PSCATTER_GATHER_LIST pSGL;
PVOID SGListBuffer;
pSGL = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo);
ASSERT(pSGL != NULL);
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo) = NULL;
ASSERT(CURRENT_IRQL == DISPATCH_LEVEL);
Miniport->SystemAdapterObject->DmaOperations->PutScatterGatherList(Miniport->SystemAdapterObject,
pSGL,
TRUE);
if (NdisGetPacketFlags(Packet) & NDIS_FLAGS_USES_SG_BUFFER_LIST)
{
NdisClearPacketFlags(Packet, NDIS_FLAGS_USES_SG_BUFFER_LIST);
SGListBuffer = NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet);
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = NULL;
ASSERT(SGListBuffer != NULL);
ExFreeToNPagedLookasideList(Miniport->SGListLookasideList, SGListBuffer);
}
else if (NdisGetPacketFlags(Packet) & NDIS_FLAGS_DOUBLE_BUFFERED)
{
PNDIS_BUFFER DoubleBuffer;
PVOID Buffer;
NdisClearPacketFlags(Packet, NDIS_FLAGS_DOUBLE_BUFFERED);
DoubleBuffer = NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet);
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = NULL;
ASSERT(DoubleBuffer != NULL);
Buffer = MmGetMdlVirtualAddress(DoubleBuffer);
NdisFreeBuffer(DoubleBuffer);
FREE_POOL(Buffer);
}
}
VOID
ndisMProcessSGList(
IN PDEVICE_OBJECT pDO,
IN PIRP pIrp,
IN PSCATTER_GATHER_LIST pSGL,
IN PVOID Context
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PNDIS_PACKET Packet = (PNDIS_PACKET)Context;
PNDIS_CO_VC_PTR_BLOCK VcPtr;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_OPEN_BLOCK Open;
PNDIS_STACK_RESERVED NSR;
ASSERT(CURRENT_IRQL == DISPATCH_LEVEL);
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo) = pSGL;
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR);
Open = NSR->Open;
Miniport = Open->MiniportHandle;
//
// Handle Send/SendPacket differently
//
MINIPORT_SET_PACKET_FLAG(Packet, fPACKET_PENDING);
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO))
{
VcPtr = NSR->VcPtr;
(*VcPtr->WCoSendPacketsHandler)(VcPtr->MiniportContext,
&Packet,
1);
}
else if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_PACKET_ARRAY))
{
//
// Pass the packet down to the miniport.
//
(Open->WSendPacketsHandler)(Miniport->MiniportAdapterContext,
&Packet,
1);
}
else
{
ULONG Flags;
NDIS_STATUS Status;
NdisQuerySendFlags(Packet, &Flags);
Status = (Open->WSendHandler)(Open->MiniportAdapterContext, Packet, Flags);
if (Status != NDIS_STATUS_PENDING)
{
ndisMSendCompleteX(Miniport, Packet, Status);
}
}
}
VOID
FASTCALL
ndisMAllocSGListS(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
Allocate SG list for packets sent on a serialized miniport
Arguments:
Miniport
Packet
Return Value:
None.
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PNDIS_BUFFER Buffer;
PVOID pBufferVa;
ULONG PacketLength;
PNDIS_BUFFER pNdisBuffer = NULL;
PVOID SGListBuffer;
KIRQL OldIrql;
NdisQueryPacket(Packet, NULL, NULL, &Buffer, &PacketLength);
pBufferVa = MmGetMdlVirtualAddress(Buffer);
SGListBuffer = ExAllocateFromNPagedLookasideList(Miniport->SGListLookasideList);
//
// Callers of GetScatterGatherList must be at dispatch.
//
RAISE_IRQL_TO_DISPATCH(&OldIrql);
if (SGListBuffer)
{
Packet->Private.Flags = NdisGetPacketFlags(Packet) | NDIS_FLAGS_USES_SG_BUFFER_LIST;
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = SGListBuffer;
Status = Miniport->SystemAdapterObject->DmaOperations->BuildScatterGatherList(
Miniport->SystemAdapterObject,
Miniport->DeviceObject,
Buffer,
pBufferVa,
PacketLength,
ndisMProcessSGListS,
Packet,
TRUE,
SGListBuffer,
Miniport->ScatterGatherListSize);
if (!NT_SUCCESS(Status))
{
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = NULL;
NdisClearPacketFlags(Packet, NDIS_FLAGS_USES_SG_BUFFER_LIST);
ExFreeToNPagedLookasideList(Miniport->SGListLookasideList, SGListBuffer);
}
}
else
{
Status = NDIS_STATUS_RESOURCES;
}
if (!NT_SUCCESS(Status))
{
Status = Miniport->SystemAdapterObject->DmaOperations->GetScatterGatherList(
Miniport->SystemAdapterObject,
Miniport->DeviceObject,
Buffer,
pBufferVa,
PacketLength,
ndisMProcessSGListS,
Packet,
TRUE);
}
LOWER_IRQL(OldIrql, DISPATCH_LEVEL);
if (!NT_SUCCESS(Status))
{
PCHAR NewBuffer;
UINT BytesCopied;
//
// probably the packet was too fragmented and we couldn't allocate enough
// map registers. try to double buffer the packet.
//
do
{
//
// Allocate a buffer for the packet.
//
NewBuffer = (PUCHAR)ALLOC_FROM_POOL(PacketLength, NDIS_TAG_DOUBLE_BUFFER_PKT);
if (NULL == NewBuffer)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Allocate an MDL for the buffer
//
NdisAllocateBuffer(&Status, &pNdisBuffer, NULL, (PVOID)NewBuffer, PacketLength);
if (NDIS_STATUS_SUCCESS != Status)
{
break;
}
ndisMCopyFromPacketToBuffer(Packet, // Packet to copy from.
0, // Offset from beginning of packet.
PacketLength, // Number of bytes to copy.
NewBuffer, // The destination buffer.
&BytesCopied); // The number of bytes copied.
Packet->Private.Flags = NdisGetPacketFlags(Packet) | NDIS_FLAGS_DOUBLE_BUFFERED;
pBufferVa = MmGetMdlVirtualAddress(pNdisBuffer);
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = pNdisBuffer;
RAISE_IRQL_TO_DISPATCH(&OldIrql);
Status = Miniport->SystemAdapterObject->DmaOperations->GetScatterGatherList(
Miniport->SystemAdapterObject,
Miniport->DeviceObject,
pNdisBuffer,
pBufferVa,
PacketLength,
ndisMProcessSGListS,
Packet,
TRUE);
LOWER_IRQL(OldIrql, DISPATCH_LEVEL);
}while (FALSE);
if (!NT_SUCCESS(Status))
{
PNDIS_STACK_RESERVED NSR;
DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
("ndisMAllocSGList: GetScatterGatherList failed %lx\n", Status));
if (pNdisBuffer)
{
NdisFreeBuffer(pNdisBuffer);
}
if (NewBuffer)
{
FREE_POOL(NewBuffer);
}
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo) = NULL;
NDIS_DOUBLE_BUFFER_INFO_FROM_PACKET(Packet) = NULL;
NdisClearPacketFlags(Packet, NDIS_FLAGS_DOUBLE_BUFFERED);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
//
// complete the send, don't unlink the packet since it was never
// linked to begin with.
//
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR);
NDISM_COMPLETE_SEND_SG(Miniport, Packet, NSR, Status, TRUE, 0, FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
}
}
VOID
ndisMProcessSGListS(
IN PDEVICE_OBJECT pDO,
IN PIRP pIrp,
IN PSCATTER_GATHER_LIST pSGL,
IN PVOID Context
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PNDIS_PACKET Packet = (PNDIS_PACKET)Context;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_OPEN_BLOCK Open;
PNDIS_STACK_RESERVED NSR;
BOOLEAN LocalLock;
ASSERT(CURRENT_IRQL == DISPATCH_LEVEL);
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo) = pSGL;
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR);
Open = NSR->Open;
Miniport = Open->MiniportHandle;
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC (Miniport);
//
// queue the packet
//
LINK_PACKET_SG(Miniport, Packet, NSR);
if (Miniport->FirstPendingPacket == NULL)
{
Miniport->FirstPendingPacket = Packet;
}
//
// If we have the local lock and there is no
// packet pending, then fire off a send.
//
LOCK_MINIPORT(Miniport, LocalLock);
NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL);
if (LocalLock)
{
NDISM_PROCESS_DEFERRED(Miniport);
}
UNLOCK_MINIPORT(Miniport, LocalLock);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}