/*++ 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 #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 = 0, 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)); ASSERT(Miniport->SystemAdapterObject == NULL); 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; } //1 check for an upper bound on Miniport-> SGMapRegistersNeeded 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; if (Miniport->SystemAdapterObject == NULL) { // // 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; } if (Miniport->SGListLookasideList == NULL) { 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; UINT 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)) { PUCHAR 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. if (BytesCopied != PacketLength) { Status = NDIS_STATUS_RESOURCES; break; } 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; UNREFERENCED_PARAMETER(pDO); UNREFERENCED_PARAMETER(pIrp); 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; UINT 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)) { PUCHAR 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. if (BytesCopied != PacketLength) { Status = NDIS_STATUS_RESOURCES; break; } 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; UNREFERENCED_PARAMETER(pDO); UNREFERENCED_PARAMETER(pIrp); 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); }