|
|
/*++
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
|