/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: dma.c Abstract: This is the NT Video port driver dma support module. Author: Bruce McQuistan (brucemc) Mar. 1996 Environment: kernel mode only Notes: Revision History: --*/ #include "videoprt.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, VideoPortGetCommonBuffer) #pragma alloc_text(PAGE, VideoPortFreeCommonBuffer) #pragma alloc_text(PAGE, VideoPortDoDma) #pragma alloc_text(PAGE, VideoPortUnlockPages) #pragma alloc_text(PAGE, VideoPortSetBytesUsed) #pragma alloc_text(PAGE, VideoPortMapDmaMemory) #pragma alloc_text(PAGE, VideoPortUnmapDmaMemory) #pragma alloc_text(PAGE, VideoPortGetDmaAdapter) #pragma alloc_text(PAGE, VideoPortPutDmaAdapter) #pragma alloc_text(PAGE, VideoPortAllocateCommonBuffer) #pragma alloc_text(PAGE, VideoPortReleaseCommonBuffer) #pragma alloc_text(PAGE, VideoPortLockBuffer) #endif #define MAX_COMMON_BUFFER_SIZE 0x40000 PVOID VideoPortAllocateContiguousMemory( IN PVOID HwDeviceExtension, IN ULONG NumberOfBytes, IN PHYSICAL_ADDRESS HighestAcceptableAddress ) { if ((NumberOfBytes > MAX_COMMON_BUFFER_SIZE)) return NULL; return MmAllocateContiguousMemory(NumberOfBytes, HighestAcceptableAddress); } PVOID VideoPortGetCommonBuffer( IN PVOID HwDeviceExtension, IN ULONG DesiredLength, IN ULONG Alignment, OUT PPHYSICAL_ADDRESS LogicalAddress, OUT PULONG ActualLength, IN BOOLEAN CacheEnabled ) /*++ Routine Description: Provides physical address visible to both device and system. Memory seen as contiguous by device. Arguments: HwDeviceExtension - device extension available to miniport. DesiredLength - size of desired memory (should be minimal). Alignment - Desired alignment of buffer, currently unused. LogicalAddress - [out] parameter which will hold physical address of of the buffer upon function return. ActualLength - Actual length of buffer. CacheEnabled - Specifies whether the allocated memory can be cached. Return Value: Virtual address of the common buffer. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); VP_DMA_ADAPTER vpDmaAdapter; PVOID VirtualAddress; if (DesiredLength > MAX_COMMON_BUFFER_SIZE) { return NULL; } vpDmaAdapter.DmaAdapterObject = fdoExtension->DmaAdapterObject; VirtualAddress = VideoPortAllocateCommonBuffer(HwDeviceExtension, &vpDmaAdapter, DesiredLength, LogicalAddress, CacheEnabled, NULL); *ActualLength = VirtualAddress ? DesiredLength : 0; return (VirtualAddress); } VOID VideoPortFreeCommonBuffer( IN PVOID HwDeviceExtension, IN ULONG Length, IN PVOID VirtualAddress, IN PHYSICAL_ADDRESS LogicalAddress, IN BOOLEAN CacheEnabled ) /*++ Routine Description: Frees memory allocated by VideoPortGetCommonBuffer. Arguments: HwDeviceExtension - device extension available to miniport. DesiredLength - size of memory allocated. Alignment - Desired liagnment of buffer, currently unused. VirtualAddress - [out] parameter which will hold virtual address of the buffer upon function return. LogicalAddress - [out] parameter which will hold physical address of of the buffer upon function return. CacheEnabled - Specifies whether the allocated memory can be cached. Return Value: VOID. --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); VP_DMA_ADAPTER vpDmaAdapter; vpDmaAdapter.DmaAdapterObject = fdoExtension->DmaAdapterObject; VideoPortReleaseCommonBuffer( HwDeviceExtension, &vpDmaAdapter, Length, LogicalAddress, VirtualAddress, CacheEnabled ); } PDMA VideoPortDoDma( IN PVOID HwDeviceExtension, IN PDMA pDma, IN DMA_FLAGS DmaFlags ) /*++ This function is obsolete. --*/ { return NULL; } PDMA VideoPortAssociateEventsWithDmaHandle( IN PVOID HwDeviceExtension, IN OUT PVIDEO_REQUEST_PACKET pVrp, IN PVOID MappedUserEvent, IN PVOID DisplayDriverEvent ) /*++ This function is obsolete. --*/ { return NULL; } BOOLEAN VideoPortLockPages( IN PVOID HwDeviceExtension, IN OUT PVIDEO_REQUEST_PACKET pVrp, IN PEVENT pMappedUserEvent, IN PEVENT pDisplayEvent, IN DMA_FLAGS DmaFlags ) /*++ Routine Description: This function is obsolete. For the purpose of compatability, we lock the memory when DmaFlags == VideoPortDmaInitOnly. But we do nothing more than that. --*/ { PMDL Mdl; pVideoDebugPrint((Error, "VideoPortLockPages is obsolete!\n")); *(PULONG_PTR)(pVrp->OutputBuffer) = (ULONG_PTR) 0; if (DmaFlags != VideoPortDmaInitOnly) { return FALSE; } Mdl = VideoPortLockBuffer( HwDeviceExtension, pVrp->InputBuffer, pVrp->InputBufferLength, VpModifyAccess ); if( Mdl == NULL ){ return FALSE; } // // Put pMdl into OutputBuffer. // *(PULONG_PTR)(pVrp->OutputBuffer) = (ULONG_PTR) Mdl; return TRUE; } BOOLEAN VideoPortUnlockPages( PVOID HwDeviceExtension, PDMA pDma ) /*++ Routine Description: This function is obsolete. For the purpose of compatability, we just unlock the memory and does nothing more than that. --*/ { PMDL Mdl = (PMDL) pDma; pVideoDebugPrint((Error, "VideoPortUnLockPages is obsolete!\n")); VideoPortUnlockBuffer( HwDeviceExtension, Mdl ); return TRUE; } PVOID VideoPortGetDmaContext( PVOID HwDeviceExtension, IN PDMA pDma ) /*++ This function is obsolete. --*/ { return NULL; } VOID VideoPortSetDmaContext( IN PVOID HwDeviceExtension, IN OUT PDMA pDma, IN PVOID InstanceContext ) /*++ This function is obsolete. --*/ { } PVOID VideoPortGetMdl( IN PVOID HwDeviceExtension, IN PDMA pDma ) /*++ Routine Description: This function is obsolete. We still return the Mdl for the purpose of compatibility. --*/ { // // pDma is the Mdl ( see VideoPortLockPages ) // return (PVOID) pDma; } ULONG VideoPortGetBytesUsed( IN PVOID HwDeviceExtension, IN PDMA pDma ) /*++ This function is obsolete. --*/ { return 0; } VOID VideoPortSetBytesUsed( IN PVOID HwDeviceExtension, IN OUT PDMA pDma, IN ULONG BytesUsed ) /*++ Routine Description: This function is obsolete. --*/ { } PDMA VideoPortMapDmaMemory( IN PVOID HwDeviceExtension, IN PVIDEO_REQUEST_PACKET pVrp, IN PHYSICAL_ADDRESS BoardAddress, IN PULONG Length, IN PULONG InIoSpace, IN PVOID MappedUserEvent, IN PVOID DisplayDriverEvent, IN OUT PVOID * VirtualAddress ) /*++ This function is obsolete. --*/ { return NULL; } BOOLEAN VideoPortUnmapDmaMemory( PVOID HwDeviceExtension, PVOID VirtualAddress, HANDLE ProcessHandle, PDMA BoardMemoryHandle ) /*++ This function is obsolete. --*/ { return FALSE; } // // New DMA code start here // PVP_DMA_ADAPTER VideoPortGetDmaAdapter( IN PVOID HwDeviceExtension, IN PVP_DEVICE_DESCRIPTION VpDeviceDescription ) /*++ Routine Description: Arguments: HwDeviceExtension - Points to the miniport driver's device extension. VpDeviceDescription - Points to a DEVICE_DESCRIPTION structure, which describes the attributes of the physical device. Return Value: Returns a pointer to a VP_DMA_ADAPTER on sucess, or NULL otherwise. --*/ { DEVICE_DESCRIPTION DeviceDescription; ULONG numberOfMapRegisters; PVP_DMA_ADAPTER VpDmaAdapter, p; PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); VpDmaAdapter = ExAllocatePoolWithTag( NonPagedPool, sizeof(VP_DMA_ADAPTER), VP_TAG ); if(!VpDmaAdapter) { return NULL; } else { RtlZeroMemory((PVOID) VpDmaAdapter, sizeof(VP_DMA_ADAPTER)); } // // Fill in DEVICE_DESCRITION with the data passed in. We also assume // the this is a busmaster device. // DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; DeviceDescription.ScatterGather = VpDeviceDescription->ScatterGather; DeviceDescription.Dma32BitAddresses = VpDeviceDescription->Dma32BitAddresses; DeviceDescription.Dma64BitAddresses = VpDeviceDescription->Dma64BitAddresses; DeviceDescription.MaximumLength = VpDeviceDescription->MaximumLength; DeviceDescription.BusNumber = fdoExtension->SystemIoBusNumber; DeviceDescription.InterfaceType = fdoExtension->AdapterInterfaceType; DeviceDescription.Master = TRUE; DeviceDescription.DemandMode = FALSE; DeviceDescription.AutoInitialize = FALSE; DeviceDescription.IgnoreCount = FALSE; DeviceDescription.Reserved1 = FALSE; DeviceDescription.DmaWidth = FALSE; DeviceDescription.DmaSpeed = FALSE; DeviceDescription.DmaPort = FALSE; DeviceDescription.DmaChannel = 0; VpDmaAdapter->DmaAdapterObject = IoGetDmaAdapter( fdoExtension->PhysicalDeviceObject, &DeviceDescription, &numberOfMapRegisters ); if(!(VpDmaAdapter->DmaAdapterObject)) { ExFreePool((PVOID)VpDmaAdapter); return NULL; } else { // // Initialize the other fields of VP_DMA_ADAPTER // VpDmaAdapter->NumberOfMapRegisters = numberOfMapRegisters; } // // Add the new VpDmaAdapter to the list // VpDmaAdapter->NextVpDmaAdapter = fdoExtension->VpDmaAdapterHead; fdoExtension->VpDmaAdapterHead = VpDmaAdapter; return(VpDmaAdapter); } VOID VideoPortPutDmaAdapter( IN PVOID HwDeviceExtension, IN PVP_DMA_ADAPTER VpDmaAdapter ) /*++ Routine Description: Arguments: HwDeviceExtension - Points to the miniport driver's device extension. VpDmaAdapter - Points to the VP_DMA_ADAPTER structure returned by VideoPortGetDmaAdapter. Return Value: Frees the resource allocated in VideoPortGetDmaAdapter --*/ { PVP_DMA_ADAPTER p, q; PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); // // Frees the DMA_ADAPTER structure allocated by IoGetDmaAdapter // DMA_OPERATION(PutDmaAdapter)(VpDmaAdapter->DmaAdapterObject); // // Remove this VpDmaAdapter from the list // p = fdoExtension->VpDmaAdapterHead; if ( p == VpDmaAdapter ) { fdoExtension->VpDmaAdapterHead = p->NextVpDmaAdapter; } else { q = p->NextVpDmaAdapter; while ( q != NULL) { if ( q == VpDmaAdapter ) { p->NextVpDmaAdapter = q->NextVpDmaAdapter; break; } p = q; q = p->NextVpDmaAdapter; } ASSERT (q); } ExFreePool((PVOID)VpDmaAdapter); } PVOID VideoPortAllocateCommonBuffer( IN PVOID HwDeviceExtension, IN PVP_DMA_ADAPTER VpDmaAdapter, IN ULONG DesiredLength, OUT PPHYSICAL_ADDRESS LogicalAddress, IN BOOLEAN CacheEnabled, OUT PVOID Reserved ) /*++ Routine Description: This function allocates and maps system memory so that it is simultaneously accessible from both the processor and a device for common-buffer DMA operations. Arguments: HwDeviceExtension - Points to the miniport driver's device extension. VpDmaAdapter - Points to the VP_DMA_ADAPTER structure returned by VideoPortGetDmaAdapter. DesiredLength - Specifies the requested number of bytes of memory. LogicalAddress - Points to a variable that receives the logical address to be used by the adapter to access the allocated buffer. CacheEnabled - Specifies whether the allocated memory can be cached. Reserved - Reserved Return Value: Returns the base virtual address of the allocated buffer if successful. Otherwise, returns NULL if the buffer cannot be allocated. --*/ { PVOID VirtualAddress; PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); if ((VpDmaAdapter == NULL) || (VpDmaAdapter->DmaAdapterObject == NULL)) { pVideoDebugPrint((Error, "VideoPortAllocateCommonBuffer: Invalid DMA adapter!\n")); ASSERT(FALSE); return NULL; } VirtualAddress = DMA_OPERATION(AllocateCommonBuffer)(VpDmaAdapter->DmaAdapterObject, DesiredLength, LogicalAddress, CacheEnabled); if (Reserved) { *(PULONG)Reserved = VirtualAddress ? DesiredLength : 0; pVideoDebugPrint((Error, "VideoPortAllocateCommonBuffer: The last parameter of this function is reserved and should be set to NULL!\n")); } return VirtualAddress; } VOID VideoPortReleaseCommonBuffer( IN PVOID HwDeviceExtension, IN PVP_DMA_ADAPTER VpDmaAdapter, IN ULONG Length, IN PHYSICAL_ADDRESS LogicalAddress, IN PVOID VirtualAddress, IN BOOLEAN CacheEnabled ) /*++ Routine Description: This function frees a common buffer allocated by VideoPortAllocateCommonBuffer Arguments: HwDeviceExtension - Points to the miniport driver's device extension. VpDmaAdapter - Points to the VP_DMA_ADAPTER structure returned by VideoPortGetDmaAdapter. Length - Specifies the number of bytes of memory to be freed. LogicalAddress - Specifies the logical address of the buffer to be freed. VirtualAddress - Points to the corresponding virtual address of the allocated memory range. CacheEnabled - Specifies whether the allocated memory can be cached. Return Value: None --*/ { PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); if ((VpDmaAdapter == NULL) || (VpDmaAdapter->DmaAdapterObject == NULL)) { pVideoDebugPrint((Error, " VideoPortReleaseCommonBuffer: Invalid DMA Adapter!\n" )); ASSERT(FALSE); return; } DMA_OPERATION(FreeCommonBuffer)( VpDmaAdapter->DmaAdapterObject, Length, LogicalAddress, VirtualAddress, CacheEnabled ); } PVOID VideoPortLockBuffer( IN PVOID HwDeviceExtension, IN PVOID BaseAddress, IN ULONG Length, IN VP_LOCK_OPERATION Operation ) /*++ Routine Description: This function probes specified buffer, makes them resident, and locks the physical pages mapped by the virtual address range in memory. Arguments: HwDeviceExtension - Points to the miniport driver's device extension. BaseAddress - Virtual address of the buffer to be locked. Length - Specifies the length in bytes of the buffer to be locked. Operation - Specifies the type of operation for which the caller wants the access rights probed and the pages locked, one of VpReadAccess, VpWriteAccess, or VpModifyAccess. Return Value: Returns a pointer to an MDL, or NULL if the MDL cannot be allocated. --*/ { PMDL Mdl; // // Allocate the MDL, but don't stuff it in the Irp, as IoCompleteRequest // will free it! // Mdl = IoAllocateMdl(BaseAddress, Length, FALSE, FALSE, NULL); if (!Mdl) { pVideoDebugPrint((Warn, "VideoPortLockBuffer: No MDL address!\n")); return NULL; } // // Lock down the users buffer // __try { MmProbeAndLockPages( Mdl, KernelMode, Operation ); } __except(EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(Mdl); pVideoDebugPrint((Error, "VideoPortLockBuffer: MmProbeandLockPages exception\n")); Mdl = NULL; } return Mdl; } VOID VideoPortUnlockBuffer( IN PVOID HwDeviceExtension, IN PVOID Mdl ) /*++ Routine Description: This function unlocks physical pages described by a given MDL. Arguments: HwDeviceExtension - Points to the miniport driver's device extension. Mdl - A Pointer that returned from VideoPortLockBuffer. Return Value: None --*/ { if(Mdl == NULL) { ASSERT(FALSE); return; } MmUnlockPages(Mdl); IoFreeMdl(Mdl); } typedef struct __LIST_CONTROL_CONTEXT { PVOID MiniportContext; PVOID HwDeviceExtension; PVP_DMA_ADAPTER VpDmaAdapter; PEXECUTE_DMA ExecuteDmaRoutine; PVP_SCATTER_GATHER_LIST VpScatterGather; } LIST_CONTROL_CONTEXT, *PLIST_CONTROL_CONTEXT; VP_STATUS VideoPortStartDma( IN PVOID HwDeviceExtension, IN PVP_DMA_ADAPTER VpDmaAdapter, IN PVOID Mdl, IN ULONG Offset, IN OUT PULONG pLength, IN PEXECUTE_DMA ExecuteDmaRoutine, IN PVOID MiniportContext, IN BOOLEAN WriteToDevice ) /*++ Routine Description: This function flushes the memory from caches of host processors and calls GetScatterGatherList to build scatter/gather list Arguments: HwDeviceExtension - Points to the miniport driver's device extension. VpDmaAdapter - Points to the VP_DMA_ADAPTER structure returned by VideoPortGetDmaAdapter. Mdl - Points to the MDL that describes the buffer Offset - The byte offset in the buffer from where DMA operation starts. pLength - Specifies the requested transfer size in bytes. On return, this points to the actual size to be transferred. ExecuteDmaRoutine - Points to a miniport driver-supplied ExecuteDmaRoutine routine which will be called to program hardware registers to start actual DMA operation. MiniportContext - Points to the miniport driver-determined context to be passed to the ExecuteDmaRoutine. WriteToDevice - Indicates the direction of the DMA transfer: TRUE for a transfer from the buffer to the device, and FALSE otherwise. Return Value: VP_STATUS --*/ { KIRQL currentIrql; ULONG NumberOfMapRegisters; NTSTATUS ntStatus; PLIST_CONTROL_CONTEXT Context; PVOID CurrentVa; PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension); Context = ( PLIST_CONTROL_CONTEXT ) ExAllocatePoolWithTag ( NonPagedPool, sizeof(LIST_CONTROL_CONTEXT), VP_TAG ); if (Context == NULL) { *pLength = 0; return ERROR_NOT_ENOUGH_MEMORY; } // // Flush the buffer // KeFlushIoBuffers( Mdl, !WriteToDevice, TRUE ); // // Calculate the number of map registers needed. // CurrentVa = (PVOID)((PUCHAR)MmGetMdlVirtualAddress((PMDL)Mdl) + Offset); NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES (CurrentVa, *pLength); // // If the number of map registers needed is greater than the maximum // number we can handle, we will do a partial transfer. // // We updated *pLength here so that it is safe to check this value // when the miniport callback routine get called. // if (NumberOfMapRegisters > VpDmaAdapter->NumberOfMapRegisters) { NumberOfMapRegisters = VpDmaAdapter->NumberOfMapRegisters; *pLength = NumberOfMapRegisters * PAGE_SIZE - BYTE_OFFSET(CurrentVa); } // // Prepare Context for pVideoPortListControl // Context->HwDeviceExtension = HwDeviceExtension; Context->MiniportContext = MiniportContext; Context->VpDmaAdapter = VpDmaAdapter; Context->ExecuteDmaRoutine = ExecuteDmaRoutine; // // Call GetScatterGatherList which will call pVideoPortListControl to // build scatter-gather list // KeRaiseIrql( DISPATCH_LEVEL, ¤tIrql ); ntStatus = DMA_OPERATION(GetScatterGatherList) ( VpDmaAdapter->DmaAdapterObject, // AdapterObject fdoExtension->FunctionalDeviceObject, // DeviceObject Mdl, // Mdl CurrentVa, // CurrentVa *pLength, // Transfer Size pVideoPortListControl, // ExecutionRoutine Context, // Context WriteToDevice ); // WriteToDevice KeLowerIrql(currentIrql); if(!NT_SUCCESS(ntStatus)) { *pLength = 0; ExFreePool((PVOID) Context); return ERROR_NOT_ENOUGH_MEMORY; } return NO_ERROR; } BOOLEAN pVideoPortSynchronizeExecuteDma( PLIST_CONTROL_CONTEXT Context ) { (Context->ExecuteDmaRoutine)( Context->HwDeviceExtension, Context->VpDmaAdapter, Context->VpScatterGather, Context->MiniportContext ); return TRUE; } VOID pVideoPortListControl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PSCATTER_GATHER_LIST ScatterGather, IN PVOID ListControlContext ) /*++ Routine Description: Get scatter/gather list and calls the miniport callback function to start actual DMA transfer Arguments: Return Value: None --*/ { PLIST_CONTROL_CONTEXT Context; PFDO_EXTENSION fdoExtension; PVP_SCATTER_GATHER_LIST VpScatterGather; Context = (PLIST_CONTROL_CONTEXT)ListControlContext; fdoExtension = GET_FDO_EXT(Context->HwDeviceExtension); VpScatterGather = (PVP_SCATTER_GATHER_LIST )(ScatterGather); Context->VpScatterGather = VpScatterGather; VideoPortSynchronizeExecution( fdoExtension->HwDeviceExtension, VpMediumPriority, pVideoPortSynchronizeExecuteDma, Context ); ExFreePool((PVOID) Context); } VP_STATUS VideoPortCompleteDma( IN PVOID HwDeviceExtension, IN PVP_DMA_ADAPTER VpDmaAdapter, IN PVP_SCATTER_GATHER_LIST VpScatterGather, IN BOOLEAN WriteToDevice ) /*++ Routine Description: This function flushs the adapter buffers, frees the map registers and frees the scatter/gather list previously allocated by GetScatterGatherList. Arguments: HwDeviceExtension - Points to the miniport driver's device extension. VpScatterGather - Points to a scatter/gather list previously passed to miniport callback routine ExecuteDmaRoutine. WriteToDevice - Indicates the direction of the DMA transfer: specify TRUE for a transfer from the buffer to the device, and FALSE otherwise. --*/ { KIRQL currentIrql; // // Call PutScatterGatherList to flush the adapter buffers, free // the map registers and the scatter/gather list previously // allocated by GetScatterGatherList. // KeRaiseIrql( DISPATCH_LEVEL, ¤tIrql ); DMA_OPERATION(PutScatterGatherList)( VpDmaAdapter->DmaAdapterObject, (PSCATTER_GATHER_LIST)VpScatterGather, WriteToDevice ); KeLowerIrql(currentIrql); return NO_ERROR; } #if DBG VOID pDumpScatterGather(PVP_SCATTER_GATHER_LIST SGList) { PVP_SCATTER_GATHER_ELEMENT Element; LONG i; pVideoDebugPrint((Info, "NumberOfElements = %d\n", SGList->NumberOfElements)); Element = SGList->Elements; for(i = 0; i < (LONG)(SGList->NumberOfElements); i++) { pVideoDebugPrint((Error, "Length = 0x%x, Address = 0x%x\n", Element[i].Length, Element[i].Address)); } } #endif // DBG