www.pudn.com > 9054Src.rar > SupportFunc.c
/*******************************************************************************
* Copyright (c) 2006 PLX Technology, Inc.
*
* PLX Technology Inc. licenses this software under specific terms and
* conditions. Use of any of the software or derviatives thereof in any
* product without a PLX Technology chip is strictly prohibited.
*
* PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY,
* EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. PLX makes no guarantee
* or representations regarding the use of, or the results of the use of,
* the software and documentation in terms of correctness, accuracy,
* reliability, currentness, or otherwise; and you rely on the software,
* documentation and results solely at your own risk.
*
* IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS,
* LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES
* OF ANY KIND. IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM
* PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER.
*
******************************************************************************/
/******************************************************************************
*
* File Name:
*
* SupportFunc.c
*
* Description:
*
* Additional support functions
*
* Revision History:
*
* 03-01-06 : PCI SDK v4.40
*
******************************************************************************/
#include "ApiFunctions.h"
#include "CommonApi.h"
#include "GlobalVars.h"
#include "PciSupport.h"
#include "SupportFunc.h"
/*********************************************************************
*
* Function : PlxSynchronizedRegisterModify
*
* Description: Register modify to be called with KeSynchronizeExecution()
*
********************************************************************/
BOOLEAN
PlxSynchronizedRegisterModify(
PLX_REG_DATA *pRegData
)
{
U32 RegValue;
RegValue =
PLX_REG_READ(
pRegData->pdx,
pRegData->offset
);
RegValue |= pRegData->BitsToSet;
RegValue &= ~(pRegData->BitsToClear);
PLX_REG_WRITE(
pRegData->pdx,
pRegData->offset,
RegValue
);
return TRUE;
}
/******************************************************************************
*
* Function : PlxSignalNotifications
*
* Description: Called by the ISR to signal any notification events
*
* Note : This is expected to be called at DPC level
*
******************************************************************************/
VOID
PlxSignalNotifications(
DEVICE_EXTENSION *pdx,
U32 IntSource
)
{
PLIST_ENTRY pEntry;
PLX_WAIT_OBJECT *pWaitObject;
KeAcquireSpinLockAtDpcLevel(
&(pdx->Lock_WaitObjectsList)
);
// Get the interrupt wait list
pEntry = pdx->List_WaitObjects.Flink;
// Traverse wait objects and wake-up processes
while (pEntry != &(pdx->List_WaitObjects))
{
// Get the wait object
pWaitObject =
CONTAINING_RECORD(
pEntry,
PLX_WAIT_OBJECT,
ListEntry
);
// Check if waiting for active interrupt
if (pWaitObject->NotifyOnInterrupt & IntSource)
{
DebugPrintf((
"DPC signaling wait object (%p)\n",
pWaitObject
));
// Save the new interrupt source in case later requested
pWaitObject->IntSource |= pWaitObject->NotifyOnInterrupt & IntSource;
// Signal wait object
KeSetEvent(
pWaitObject->pKEvent,
IO_NO_INCREMENT,
FALSE
);
}
// Jump to next item in the list
pEntry = pEntry->Flink;
}
KeReleaseSpinLockFromDpcLevel(
&(pdx->Lock_WaitObjectsList)
);
}
/*********************************************************************
*
* Function : PlxRegistryInformationGet
*
* Description: Gets driver configuration information from the registry
*
********************************************************************/
VOID
PlxRegistryInformationGet(
UNICODE_STRING *pRegistryPath,
REGISTRY_INFORMATION *pRegistryInfo
)
{
NTSTATUS status;
RTL_QUERY_REGISTRY_TABLE RegTable[2];
#if defined(PLX_NT_DRIVER)
U32 *marker;
WCHAR slBuffer[200];
UNICODE_STRING supportedList;
UNICODE_STRING fragment;
#endif
RtlZeroMemory(
RegTable,
sizeof(RegTable)
);
RegTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT;
// Get Common DMA buffer size
RegTable[0].Name = L"CommonBufferSize";
RegTable[0].EntryContext = &(pRegistryInfo->CommonBufferSize);
status =
RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
pRegistryPath->Buffer,
RegTable,
NULL,
NULL
);
if ( !NT_SUCCESS(status) )
{
pRegistryInfo->CommonBufferSize = DEFAULT_SIZE_COMMON_BUFFER;
}
// Get cacheability of physical memory buffers
RegTable[0].Name = L"PhysicalMemoryCacheable";
RegTable[0].EntryContext = &(pRegistryInfo->PhysicalMemoryCacheable);
status =
RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
pRegistryPath->Buffer,
RegTable,
NULL,
NULL
);
if ( !NT_SUCCESS(status) )
{
pRegistryInfo->PhysicalMemoryCacheable = DEFAULT_PHYS_MEM_CACHE_ENABLED;
}
else
{
if (pRegistryInfo->PhysicalMemoryCacheable == 0)
{
pRegistryInfo->PhysicalMemoryCacheable = FALSE;
}
else
{
pRegistryInfo->PhysicalMemoryCacheable = TRUE;
}
}
#if defined(PLX_NT_DRIVER)
// Get list of Valid IDs
supportedList.MaximumLength = sizeof(slBuffer);
supportedList.Buffer = slBuffer;
RegTable[0].Name = L"SupportedIDs";
RegTable[0].EntryContext = &supportedList;
status =
RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
pRegistryPath->Buffer,
RegTable,
NULL,
NULL
);
if ( !NT_SUCCESS(status) )
{
DebugPrintf((
"ERROR - Unable to read %ws registry entry\n",
RegTable[0].Name
));
pRegistryInfo->ValidIdList[0] = PLX_DEFAULT_DEV_VEN_ID;
pRegistryInfo->IdListSize = 1;
}
else
{
// Now parse the unicode list and place the ID values into the array
marker = pRegistryInfo->ValidIdList;
pRegistryInfo->IdListSize = 0;
fragment.Buffer = supportedList.Buffer;
fragment.MaximumLength = sizeof(WCHAR) * 8;
fragment.Length = sizeof(WCHAR) * 8;
while (1)
{
RtlUnicodeStringToInteger(
&fragment,
0x10,
marker
);
fragment.Buffer += 9; /* must skip to next value +1 for space */
marker++;
(pRegistryInfo->IdListSize) += 1;
if ((U32)fragment.Buffer >= (U32)supportedList.Buffer + (U32)supportedList.Length)
break;
}
}
#endif
}
/******************************************************************************
*
* Function : GetBarIndex
*
* Description: Associate a h/w resource with a bar number
*
******************************************************************************/
U8
GetBarIndex(
PHYSICAL_ADDRESS address,
PCI_COMMON_CONFIG *pPciRegs
)
{
U8 i;
U32 CompareAddress;
// Compare the physical address with each BAR
for (i = 0; i < PCI_NUM_BARS_TYPE_00; i++)
{
if (pPciRegs->u.type0.BaseAddresses[i] & 0x1)
CompareAddress = pPciRegs->u.type0.BaseAddresses[i] & 0xFFFFFFFC;
else
CompareAddress = pPciRegs->u.type0.BaseAddresses[i] & 0xFFFFFFF0;
if (address.u.LowPart == CompareAddress)
return i;
}
// Unable to find the BAR index
DebugPrintf((
"ERROR - GetBarIndex() unable to match BAR value (0x%08lx)\n",
address
));
return (U8)-1;
}
/******************************************************************************
*
* Function : PlxCompleteIrp
*
* Description: Complete an IRP
*
******************************************************************************/
NTSTATUS
PlxCompleteIrp(
PIRP pIrp,
NTSTATUS status
)
{
pIrp->IoStatus.Status = status;
IoCompleteRequest(
pIrp,
IO_NO_INCREMENT
);
if (status == STATUS_CANCELLED)
DebugPrintf(("...Cancelled IRP (0x%p)\n", pIrp));
else
DebugPrintf(("...Completed IRP (0x%p)\n", pIrp));
return status;
}
/******************************************************************************
*
* Function : PlxCompleteIrpWithInformation
*
* Description: Complete an IRP including modification of the Information field
*
******************************************************************************/
NTSTATUS
PlxCompleteIrpWithInformation(
PIRP pIrp,
NTSTATUS status,
PLX_UINT_PTR Info
)
{
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = Info;
IoCompleteRequest(
pIrp,
IO_NO_INCREMENT
);
if (status == STATUS_CANCELLED)
DebugPrintf(("...Cancelled IRP (0x%p)\n", pIrp));
else
DebugPrintf(("...Completed IRP (0x%p)\n", pIrp));
return status;
}
/******************************************************************************
*
* Function : Plx_sleep
*
* Description: Function as a normal sleep. Parameter is in millisecond
*
******************************************************************************/
VOID
Plx_sleep(
U32 delay
)
{
LARGE_INTEGER liTime;
/* Convert milliseconds to 100-nanosecond increments using:
*
* 1 ns = 10 ^ -9 sec
* 100 ns = 10 ^ -7 sec (1 timer interval)
* 1 ms = 10 ^ -3 sec
* 1 ms = (1 timer interval) * 10^4
*/
delay = delay * 10000;
// Negative value means relative time, not absolute
liTime =
RtlConvertLongToLargeInteger(
-(LONG)delay
);
KeDelayExecutionThread(
KernelMode,
TRUE,
&liTime
);
}
/******************************************************************************
*
* Function : PlxPciBarResourceMap
*
* Description: Maps a PCI BAR resource into kernel space
*
******************************************************************************/
NTSTATUS
PlxPciBarResourceMap(
DEVICE_EXTENSION *pdx,
U8 BarIndex
)
{
U32 SizeToMap;
U32 SizeRemain;
VOID *pVa;
PMDL pMdl;
PMDL pMdl_Previous;
// Map into Kernel Virtual Space
pdx->PciBar[BarIndex].pVa =
MmMapIoSpace(
pdx->PciBar[BarIndex].Physical,
pdx->PciBar[BarIndex].Size,
MmNonCached
);
if (pdx->PciBar[BarIndex].pVa == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/******************************************************************
* A future mapping into user space will require an MDL. Although
* it is not documented in the Windows DDK, a single MDL is limited
* to describing just under 64MB. This is because IoAllocateMdl
* only allocates 64k for the MDL, which includes space for the
* physical page addresses that follow the MDL.
*
* In that case, we must allocate multiple MDLs and connect them
* by using the "Next" field of the MDL.
*****************************************************************/
// Set initial values
pVa = pdx->PciBar[BarIndex].pVa;
SizeRemain = pdx->PciBar[BarIndex].Size;
while (SizeRemain != 0)
{
// Determine size for MDL
if (SizeRemain <= MAX_MDL_SIZE)
{
SizeToMap = SizeRemain;
}
else
{
SizeToMap = MAX_MDL_SIZE;
}
// Get an MDL for future mapping into user space
pMdl =
IoAllocateMdl(
pVa,
SizeToMap,
FALSE,
FALSE,
NULL
);
// Check if the MDL allocation succeeded
if (pMdl == NULL)
{
DebugPrintf(("ERROR - MDL allocation for space failed\n"));
// Release any created MDLs
while (pdx->PciBar[BarIndex].pMdl != NULL)
{
pMdl = pdx->PciBar[BarIndex].pMdl;
pdx->PciBar[BarIndex].pMdl = pMdl->Next;
IoFreeMdl(
pMdl
);
}
return STATUS_SUCCESS;
}
else
{
// Build the MDL
MmBuildMdlForNonPagedPool(
pMdl
);
}
// Clear next MDL pointer
pMdl->Next = NULL;
// Store MDL
if (pdx->PciBar[BarIndex].pMdl == NULL)
{
// Insert first MDL
pdx->PciBar[BarIndex].pMdl = pMdl;
pMdl_Previous = pMdl;
}
else
{
// Add new MDL to list
pMdl_Previous->Next = pMdl;
// Update previous pointer
pMdl_Previous = pMdl;
}
// Adjust for next virtual address
(U8*)pVa += SizeToMap;
// Adjust remaining count
SizeRemain -= SizeToMap;
}
return STATUS_SUCCESS;
}
/******************************************************************************
*
* Function : PlxPciBarResourcesUnmap
*
* Description: Unmap all mapped PCI BAR memory for a device
*
******************************************************************************/
VOID
PlxPciBarResourcesUnmap(
DEVICE_EXTENSION *pdx
)
{
U8 i;
PMDL pMdl;
// Go through all the BARS
for (i = 0; i < PCI_NUM_BARS_TYPE_00; i++)
{
// Unmap the space from Kernel space if previously mapped
if ((pdx->PciBar[i].bIsIoSpace == FALSE) &&
(pdx->PciBar[i].Physical.QuadPart != 0))
{
DebugPrintf(("Unmapping BAR %d...\n", i));
// Release the MDLs describing the BAR space
while (pdx->PciBar[i].pMdl != NULL)
{
pMdl = pdx->PciBar[i].pMdl;
pdx->PciBar[i].pMdl = pMdl->Next;
IoFreeMdl(
pMdl
);
}
// Unmap from kernel space
if (pdx->PciBar[i].pVa != NULL)
{
MmUnmapIoSpace(
pdx->PciBar[i].pVa,
pdx->PciBar[i].Size
);
pdx->PciBar[i].pVa = NULL;
}
}
}
}
/******************************************************************************
*
* Function : PlxPciBarSpaceUnmapAll_ByOwner
*
* Description: Unmap any PCI BAR spaces assigned to the specified owner
*
******************************************************************************/
VOID
PlxPciBarSpaceUnmapAll_ByOwner(
DEVICE_EXTENSION *pdx,
VOID *pOwner
)
{
VOID *UserVa;
KIRQL IrqL_Original;
PLIST_ENTRY pEntry;
PLX_USER_MAPPING *pMapObject;
KeAcquireSpinLock(
&(pdx->Lock_BarMappingsList),
&IrqL_Original
);
pEntry = pdx->List_BarMappings.Flink;
// Traverse list to find the desired list objects
while (pEntry != &(pdx->List_BarMappings))
{
// Get the object
pMapObject =
CONTAINING_RECORD(
pEntry,
PLX_USER_MAPPING,
ListEntry
);
// Check if owner matches
if (pMapObject->pOwner == pOwner)
{
// Copy address
UserVa = pMapObject->pUserVa[0];
// Release list lock
KeReleaseSpinLock(
&(pdx->Lock_BarMappingsList),
IrqL_Original
);
// Unmap BAR space
PlxPciBarUnmap(
pdx,
UserVa,
pOwner
);
KeAcquireSpinLock(
&(pdx->Lock_BarMappingsList),
&IrqL_Original
);
// Restart parsing the list from the beginning
pEntry = pdx->List_BarMappings.Flink;
}
else
{
// Jump to next item
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pdx->Lock_BarMappingsList),
IrqL_Original
);
}
/******************************************************************************
*
* Function : PlxPciPhysicalMemoryFreeAll_ByOwner
*
* Description: Unmap & release all physical memory assigned to the specified owner
*
******************************************************************************/
VOID
PlxPciPhysicalMemoryFreeAll_ByOwner(
DEVICE_EXTENSION *pdx,
VOID *pOwner
)
{
KIRQL IrqL_Original;
PLIST_ENTRY pEntry;
PLX_PHYSICAL_MEM PciMem;
PLX_PHYS_MEM_OBJECT *pMemObject;
KeAcquireSpinLock(
&(pdx->Lock_PhysicalMemList),
&IrqL_Original
);
pEntry = pdx->List_PhysicalMem.Flink;
// Traverse list to find the desired list objects
while (pEntry != &(pdx->List_PhysicalMem))
{
// Get the object
pMemObject =
CONTAINING_RECORD(
pEntry,
PLX_PHYS_MEM_OBJECT,
ListEntry
);
// Check if owner matches
if (pMemObject->pOwner == pOwner)
{
// Copy memory information
PciMem.PhysicalAddr = pMemObject->BusPhysical;
PciMem.Size = pMemObject->Size;
// Release list lock
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
// Release the memory & remove from list
PlxPciPhysicalMemoryFree(
pdx,
&PciMem
);
KeAcquireSpinLock(
&(pdx->Lock_PhysicalMemList),
&IrqL_Original
);
// Restart parsing the list from the beginning
pEntry = pdx->List_PhysicalMem.Flink;
}
else
{
// Jump to next item
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
}
/******************************************************************************
*
* Function : PlxPciPhysicalMemoryUnmapAll_ByOwner
*
* Description: Unmap any physical memory assigned to the specified owner
*
******************************************************************************/
VOID
PlxPciPhysicalMemoryUnmapAll_ByOwner(
DEVICE_EXTENSION *pdx,
PLX_PHYS_MEM_OBJECT *pMemObject,
VOID *pOwner
)
{
VOID *pUserVa;
KIRQL IrqL_Original;
PLIST_ENTRY pEntry;
PLX_PHYSICAL_MEM PciMem;
PHYSICAL_ADDRESS AddrPhysical;
PLX_USER_MAPPING *pMapObject;
// Verify Memory Object
if (pMemObject == NULL)
{
return;
}
// Verify size
if (pMemObject->Size == 0)
{
return;
}
// Setup memory properties
PciMem.PhysicalAddr = pMemObject->BusPhysical;
PciMem.Size = pMemObject->Size;
// Find the map object to unmap
KeAcquireSpinLock(
&(pMemObject->Lock_MappingsList),
&IrqL_Original
);
pEntry = pMemObject->List_Mappings.Flink;
// Traverse list to find the desired list object
while (pEntry != &(pMemObject->List_Mappings))
{
// Get the object
pMapObject =
CONTAINING_RECORD(
pEntry,
PLX_USER_MAPPING,
ListEntry
);
// Check if the owner matches
if ((pOwner == NULL) || (pMapObject->pOwner == pOwner))
{
// Copy virtual address
PciMem.UserAddr = (PLX_UINT_PTR)pMapObject->pUserVa[0];
// Release list lock
KeReleaseSpinLock(
&(pMemObject->Lock_MappingsList),
IrqL_Original
);
// Unmap the memory & remove from list
PlxPciPhysicalMemoryUnmap(
pdx,
&PciMem,
pMapObject->pOwner
);
KeAcquireSpinLock(
&(pMemObject->Lock_MappingsList),
&IrqL_Original
);
// Restart parsing the list from the beginning
pEntry = pMemObject->List_Mappings.Flink;
}
else
{
// Jump to next item
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pMemObject->Lock_MappingsList),
IrqL_Original
);
}
/******************************************************************************
*
* Function : PlxDmaChannelCleanup
*
* Description: Called by the Cleanup routine to close any open channels
*
******************************************************************************/
VOID
PlxDmaChannelCleanup(
DEVICE_EXTENSION *pdx,
VOID *pOwner
)
{
#if defined(DMA_SUPPORT)
U8 i;
DMA_CHANNEL channel;
// Added to avoid compiler warning
channel = PrimaryPciChannel0;
for (i = 0; i < NUMBER_OF_DMA_CHANNELS; i++)
{
// Check if terminating application is the owner
if (pdx->DmaInfo[i].pOwner == pOwner)
{
DebugPrintf(("Closing DMA channel %d\n", i));
switch (i)
{
case 0:
channel = PrimaryPciChannel0;
break;
case 1:
channel = PrimaryPciChannel1;
break;
case 2:
channel = IopChannel2;
break;
}
switch (pdx->DmaInfo[i].state)
{
case DmaStateClosed:
// DMA closed already, do nothing
break;
case DmaStateBlock:
PlxDmaBlockChannelClose(
pdx,
channel,
FALSE
);
break;
case DmaStateSgl:
PlxDmaSglChannelClose(
pdx,
channel,
FALSE
);
break;
default:
// Unknown or unsupported state
break;
}
}
}
#endif // DMA_SUPPORT
}
#if defined(DMA_SUPPORT)
/******************************************************************************
*
* Function : PlxSglDmaTransferComplete
*
* Description: Perform any necessary cleanup after an SGL DMA transfer
*
******************************************************************************/
VOID
PlxSglDmaTransferComplete(
DEVICE_EXTENSION *pdx,
U8 DmaIndex
)
{
KIRQL IrqL_Original;
if (pdx->DmaInfo[DmaIndex].bPending == FALSE)
{
DebugPrintf(("No pending SGL DMA to complete\n"));
return;
}
DebugPrintf((
"Unlocking user-mode buffer used for SGL DMA transfer...\n"
));
KeAcquireSpinLock(
&(pdx->Lock_DmaChannel),
&IrqL_Original
);
// Unlock the data buffer and free the MDL
if (pdx->DmaInfo[DmaIndex].pMdl != NULL)
{
MmUnlockPages(
pdx->DmaInfo[DmaIndex].pMdl
);
IoFreeMdl(
pdx->DmaInfo[DmaIndex].pMdl
);
pdx->DmaInfo[DmaIndex].pMdl = NULL;
}
// Clear the DMA pending flag
pdx->DmaInfo[DmaIndex].bPending = FALSE;
KeReleaseSpinLock(
&(pdx->Lock_DmaChannel),
IrqL_Original
);
}
/******************************************************************************
*
* Function : PlxLockBufferAndBuildSgl
*
* Description: Lock a user buffer and build an SGL for it
*
******************************************************************************/
U32
PlxLockBufferAndBuildSgl(
DEVICE_EXTENSION *pdx,
U8 DmaIndex,
DMA_TRANSFER_ELEMENT *pDma
)
{
U32 i;
U32 offset;
U32 BusSgl;
U32 BusSglOriginal;
U32 BusAddr;
U32 SglSize;
U32 BlockSize;
U32 LocalAddr;
U32 TotalPages;
U32 *pCurrentPage;
U32 BytesRemaining;
PMDL pMdl;
KIRQL IrqL_Original;
BOOLEAN bFlag;
PLX_UINT_PTR VaSgl;
DebugPrintf(("Building SGL descriptors for buffer...\n"));
DebugPrintf((" User VA : 0x%08lx\n", (PLX_UINT_PTR)pDma->u.UserVa));
DebugPrintf((" Local Addr: 0x%08x\n", pDma->LocalAddr));
DebugPrintf((" Size : %d bytes\n", pDma->TransferCount));
// Get the MDL for the User buffer to get the page list
pMdl =
IoAllocateMdl(
(VOID*)pDma->u.UserVa,
pDma->TransferCount,
FALSE,
FALSE,
NULL
);
if (pMdl == NULL)
{
DebugPrintf(("ERROR - Unable to allocate an MDL for the User buffer\n"));
return 0;
}
// Attempt to lock the user buffer into memory
bFlag = FALSE;
try
{
MmProbeAndLockPages(
pMdl,
UserMode,
IoModifyAccess
);
}
except(EXCEPTION_EXECUTE_HANDLER)
{
// Flag the exception
bFlag = TRUE;
}
// Verify that the pages were locked
if (bFlag)
{
DebugPrintf(("ERROR - Unable to lock user mode buffer pages\n"));
IoFreeMdl(
pMdl
);
return 0;
}
// Calculate total pages
TotalPages = (((U16)pMdl->Size - sizeof(MDL)) / sizeof(U32));
DebugPrintf((
"Page-locked %d user buffer pages...\n",
TotalPages
));
/******************************************************
* Build SGL descriptors
*
* The following code will build the SGL descriptors
* in PCI memory. There will be one descriptor for
* each page of memory since the pages are scattered
* throughout physical memory.
*****************************************************/
/*************************************************************
* Calculate memory needed for SGL descriptors
*
* Mem needed = #pages * size of descriptor
*
* Additionally, a value of 16 (10h) is added to provide a
* buffer to allow space to round up to the next 16-byte
* boundary, which is a requirement of the hardware.
************************************************************/
SglSize = (TotalPages * (4 * sizeof(U32))) + 16;
// Check if a previously allocated buffer can be re-used
if (pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa != NULL)
{
if (pdx->DmaInfo[DmaIndex].SglBuffer.Size >= SglSize)
{
// Buffer can be re-used, do nothing
DebugPrintf(("Re-using previously allocated SGL descriptor buffer\n"));
}
else
{
DebugPrintf(("Releasing previously allocated SGL descriptor buffer\n"));
// Release memory used for SGL descriptors
Plx_dma_buffer_free(
pdx,
&pdx->DmaInfo[DmaIndex].SglBuffer
);
pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa = NULL;
}
}
// Allocate memory for SGL descriptors if necessary
if (pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa == NULL)
{
DebugPrintf(("Allocating PCI memory for SGL descriptor buffer...\n"));
// Setup for transfer
pdx->DmaInfo[DmaIndex].SglBuffer.Size = SglSize;
pdx->DmaInfo[DmaIndex].SglBuffer.bCacheable = FALSE;
VaSgl =
(PLX_UINT_PTR)Plx_dma_buffer_alloc(
pdx,
&pdx->DmaInfo[DmaIndex].SglBuffer
);
if (VaSgl == 0)
{
DebugPrintf((
"ERROR - Unable to allocate %d bytes for %d SGL descriptors\n",
pdx->DmaInfo[DmaIndex].SglBuffer.Size,
TotalPages
));
MmUnlockPages(
pMdl
);
IoFreeMdl(
pMdl
);
return 0;
}
}
else
{
VaSgl = (PLX_UINT_PTR)pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa;
}
// Save MDL for deallocation after the DMA transfer
KeAcquireSpinLock(
&(pdx->Lock_DmaChannel),
&IrqL_Original
);
pdx->DmaInfo[DmaIndex].pMdl = pMdl;
KeReleaseSpinLock(
&(pdx->Lock_DmaChannel),
IrqL_Original
);
// Prepare for build of SGL
LocalAddr = pDma->LocalAddr;
// Get bus physical address of SGL descriptors
BusSgl = (U32)pdx->DmaInfo[DmaIndex].SglBuffer.BusPhysical;
// Make sure addresses are aligned on 16-byte boundary
VaSgl = (VaSgl + 0xF) & ~(0xF);
BusSgl = (BusSgl + 0xF) & ~(0xF);
// Store the starting address of the SGL for later return
BusSglOriginal = BusSgl;
DebugPrintf((
"Building SGL descriptor list located at 0x%08x...\n",
BusSglOriginal
));
// Point to first page in physical page list (located at end of MDL)
pCurrentPage = (U32*)(((U8*)pMdl) + sizeof(MDL));
// Set offset of first page
offset = pMdl->ByteOffset;
// Initialize bytes remaining
BytesRemaining = pDma->TransferCount;
// Build the SGL list
for (i = 0; i < TotalPages; i++)
{
// Calculate transfer size
if (BytesRemaining > (PAGE_SIZE - offset))
{
BlockSize = PAGE_SIZE - offset;
}
else
{
BlockSize = BytesRemaining;
}
// Get bus address of buffer
BusAddr = (*pCurrentPage << PAGE_SHIFT) + offset;
// Enable the following to display the parameters of each SGL descriptor
if (PLX_DEBUG_DISPLAY_SGL_DESCR)
{
DebugPrintf((
"SGL Desc %02d: PCI=%08Xh Loc=%08Xh Size=%Xh (%d) bytes\n",
i, (U32)BusAddr, LocalAddr, BlockSize, BlockSize
));
}
// Write PCI address in descriptor
*(((U32*)VaSgl) + SGL_DESC_IDX_PCI_ADDR) = BusAddr;
// Write Local address in descriptor
*(((U32*)VaSgl) + SGL_DESC_IDX_LOC_ADDR) = LocalAddr;
// Write transfer count in descriptor
*(((U32*)VaSgl) + SGL_DESC_IDX_COUNT) = BlockSize;
// Adjust byte count
BytesRemaining -= BlockSize;
if (BytesRemaining == 0)
{
// Write the last descriptor
*(((U32*)VaSgl) + SGL_DESC_IDX_NEXT_DESC) =
(pDma->LocalToPciDma << 3) | (1 << 1) | (1 << 0);
}
else
{
// Calculate address of next descriptor
BusSgl += (4 * sizeof(U32));
// Write next descriptor address
*(((U32*)VaSgl) + SGL_DESC_IDX_NEXT_DESC) =
BusSgl | (pDma->LocalToPciDma << 3) | (1 << 0);
// Adjust Local address
if (pdx->DmaInfo[DmaIndex].bLocalAddrConstant == FALSE)
LocalAddr += BlockSize;
// Adjust virtual address to next descriptor
VaSgl += (4 * sizeof(U32));
// Clear offset
offset = 0;
}
// Go to next page
pCurrentPage++;
}
// Make sure cache data is flushed
KeFlushIoBuffers(
pMdl,
(BOOLEAN)pDma->LocalToPciDma, // Read from device operation?
TRUE // DMA transfer?
);
// Return the physical address of the SGL
return BusSglOriginal;
}
#endif // DMA_SUPPORT
/******************************************************************************
*
* Function : Plx_dma_buffer_alloc
*
* Description: Allocates physically contiguous non-paged memory
*
******************************************************************************/
VOID*
Plx_dma_buffer_alloc(
DEVICE_EXTENSION *pdx,
PLX_PHYS_MEM_OBJECT *pMemObject
)
{
PHYSICAL_ADDRESS BufferLogicalAddress;
// Verify the DMA adapter object
if (pdx->pDmaAdapter == NULL)
{
DebugPrintf((
"ERROR - DMA Adapter object does not exist, cannot allocate physical memory\n"
));
return NULL;
}
// Attempt to allocate contiguous memory
#if defined(PLX_NT_DRIVER)
pMemObject->pKernelVa =
HalAllocateCommonBuffer(
pdx->pDmaAdapter,
pMemObject->Size,
&BufferLogicalAddress,
pMemObject->bCacheable // Enable Caching for buffer?
);
#else
pMemObject->pKernelVa =
pdx->pDmaAdapter->DmaOperations->AllocateCommonBuffer(
pdx->pDmaAdapter,
pMemObject->Size,
&BufferLogicalAddress,
pMemObject->bCacheable // Enable Caching for buffer?
);
#endif // PLX_NT_DRIVER
if (pMemObject->pKernelVa == NULL)
{
return NULL;
}
// Store the buffer logical/bus physical address
pMemObject->BusPhysical = BufferLogicalAddress.QuadPart;
// CPU Address is the same as bus on this architecture
pMemObject->CpuPhysical = BufferLogicalAddress.QuadPart;
// Clear the buffer
RtlZeroMemory(
pMemObject->pKernelVa,
pMemObject->Size
);
DebugPrintf(("Allocated physical memory...\n"));
DebugPrintf((
" CPU Phys Addr: 0x%08lx\n",
(PLX_UINT_PTR)pMemObject->CpuPhysical
));
DebugPrintf((
" Bus Phys Addr: 0x%08lx\n",
(PLX_UINT_PTR)pMemObject->BusPhysical
));
DebugPrintf((
" Kernel VA : 0x%p\n",
pMemObject->pKernelVa
));
DebugPrintf((
" Size : "
));
if (pMemObject->Size >= (10 << 10))
{
DebugPrintf_NoInfo((
"%d Kb\n",
(pMemObject->Size >> 10)
));
}
else
{
DebugPrintf_NoInfo((
"%d bytes\n",
pMemObject->Size
));
}
DebugPrintf((
" Cacheable? : %s\n",
(pMemObject->bCacheable == FALSE) ? "No" : "Yes"
));
return pMemObject->pKernelVa;
}
/******************************************************************************
*
* Function : Plx_dma_buffer_free
*
* Description: Frees previously allocated physical memory
*
******************************************************************************/
VOID
Plx_dma_buffer_free(
DEVICE_EXTENSION *pdx,
PLX_PHYS_MEM_OBJECT *pMemObject
)
{
PHYSICAL_ADDRESS BufferLogicalAddress;
// Verify the DMA adapter object
if (pdx->pDmaAdapter == NULL)
{
DebugPrintf((
"ERROR - DMA Adapter object does not exist, cannot allocate physical memory\n"
));
return;
}
// Set logical address
BufferLogicalAddress.QuadPart = pMemObject->BusPhysical;
// Release the buffer
#if defined(PLX_NT_DRIVER)
HalFreeCommonBuffer(
pdx->pDmaAdapter,
pMemObject->Size,
BufferLogicalAddress,
pMemObject->pKernelVa,
pMemObject->bCacheable // Enable Caching for buffer?
);
#else
pdx->pDmaAdapter->DmaOperations->FreeCommonBuffer(
pdx->pDmaAdapter,
pMemObject->Size,
BufferLogicalAddress,
pMemObject->pKernelVa,
pMemObject->bCacheable // Enable Caching for buffer?
);
#endif // PLX_NT_DRIVER
DebugPrintf((
"Released physical memory at 0x%08lx ",
(PLX_UINT_PTR)pMemObject->CpuPhysical
));
if (pMemObject->Size >= (10 << 10))
{
DebugPrintf_NoInfo((
"(%d Kb)\n",
(pMemObject->Size >> 10)
));
}
else
{
DebugPrintf_NoInfo((
"(%d bytes)\n",
pMemObject->Size
));
}
// Clear memory object properties
RtlZeroMemory(
pMemObject,
sizeof(PLX_PHYS_MEM_OBJECT)
);
}