www.pudn.com > 9054Src.rar > CommonApi.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:
*
* CommonApi.c
*
* Description:
*
* API functions common to all PLX chips
*
* Revision History:
*
* 03-01-06 : PCI SDK v4.40
*
******************************************************************************/
#include "ApiFunctions.h"
#include "CommonApi.h"
#include "GlobalVars.h"
#include "PciSupport.h"
#include "PlxInterrupt.h"
#include "SupportFunc.h"
/******************************************************************************
*
* Function : PlxDeviceFind
*
* Description: Search for a specific device using device location parameters
*
******************************************************************************/
RETURN_CODE
PlxDeviceFind(
DEVICE_EXTENSION *pdx,
DEVICE_LOCATION *pDeviceCriteria,
U32 *pSelection
)
{
U32 MatchFound;
BOOLEAN ContinueComparison;
DEVICE_OBJECT *fdo;
// Verify pointers
if (pDeviceCriteria == NULL || pSelection == NULL)
return ApiNullParam;
if (pdx->pDeviceObject == NULL)
return ApiFailed;
if (pdx->pDeviceObject->DriverObject == NULL)
return ApiFailed;
MatchFound = 0;
fdo = pdx->pDeviceObject->DriverObject->DeviceObject;
// Compare with items in device list
while (fdo != NULL)
{
// Get the device extension
pdx = fdo->DeviceExtension;
ContinueComparison = TRUE;
// Compare Serial Number
if ((pDeviceCriteria->SerialNumber[0] != '\0') && ContinueComparison)
{
if (_stricmp(
pdx->Device.SerialNumber,
pDeviceCriteria->SerialNumber
) != 0)
{
ContinueComparison = FALSE;
}
}
// Compare the Bus number
if ((pDeviceCriteria->BusNumber != (U8)-1) && ContinueComparison)
{
if (pDeviceCriteria->BusNumber != pdx->Device.BusNumber)
ContinueComparison = FALSE;
}
// Compare Slot Number
if ((pDeviceCriteria->SlotNumber != (U8)-1) && ContinueComparison)
{
if (pDeviceCriteria->SlotNumber != pdx->Device.SlotNumber)
ContinueComparison = FALSE;
}
// Compare Device ID
if ((pDeviceCriteria->DeviceId != (U16)-1) && ContinueComparison)
{
if (pDeviceCriteria->DeviceId != pdx->Device.DeviceId)
ContinueComparison = FALSE;
}
// Compare Vendor ID
if ((pDeviceCriteria->VendorId != (U16)-1) && ContinueComparison)
{
if (pDeviceCriteria->VendorId != pdx->Device.VendorId)
ContinueComparison = FALSE;
}
if (ContinueComparison == TRUE)
{
// Match found, check if it is the device we want
if (MatchFound++ == *pSelection)
{
// Copy the device information
RtlCopyMemory(
pDeviceCriteria,
&pdx->Device,
sizeof(DEVICE_LOCATION)
);
}
}
fdo = fdo->NextDevice;
}
// Return the number of matched devices
*pSelection = MatchFound;
return ApiSuccess;
}
/*********************************************************************
*
* Function : PlxChipTypeGet
*
* Description: Gets the PLX chip type and revision
*
*********************************************************************/
VOID
PlxChipTypeGet(
DEVICE_EXTENSION *pdx,
U32 *pChipType,
U8 *pRevision
)
{
U32 RegValue;
// Set chip type
*pChipType = PLX_CHIP_TYPE;
switch (*pChipType)
{
case 0x9080:
case 0x9054:
case 0x9056:
case 0x9656:
// Verify by reading hard-coded ID
RegValue =
PLX_REG_READ(
pdx,
0x70 // Hard-coded ID register
);
if (((RegValue & 0xFFFF) == 0x10b5) &&
((RegValue >> 16) == *pChipType))
{
// Get revision
RegValue =
PLX_REG_READ(
pdx,
0x74 // Revision ID
);
// 9054 requires additional verification
if (*pChipType != 0x9054)
{
*pRevision = (U8)RegValue;
}
else
{
// AA & AB versions have same revision ID
if (RegValue == 0xA)
{
PLX_PCI_REG_READ(
pdx,
CFG_REV_ID,
&RegValue
);
if ((RegValue & 0xf) == 0xb)
*pRevision = 0xAB;
else
*pRevision = 0xAA;
}
else
{
if (RegValue == 0xC)
{
// Set value for AC revision
*pRevision = 0xAC;
}
else
{
*pRevision = (U8)RegValue;
}
}
}
}
break;
case 0x9050:
case 0x9030:
// 9050/52 require additional verification
if (*pChipType == 0x9030)
{
*pRevision = 0xAA;
}
else
{
// Read 9050/9052 PCI revision
PLX_PCI_REG_READ(
pdx,
CFG_REV_ID,
&RegValue
);
if ((RegValue & 0xF) == 0x2)
{
*pRevision = 2;
}
else
{
*pRevision = 1;
}
}
break;
case 0x8311:
// Only AA revision exists
*pRevision = 0xAA;
break;
default:
*pChipType = 0x0;
*pRevision = 0x0;
break;
}
}
/******************************************************************************
*
* Function : PlxNotificationRegisterFor
*
* Description: Registers a wait object for notification on interrupt(s)
*
******************************************************************************/
RETURN_CODE
PlxNotificationRegisterFor(
DEVICE_EXTENSION *pdx,
PLX_INTR *pPlxIntr,
PLX_NOTIFY_OBJECT *pEvent,
VOID *pOwner
)
{
PKEVENT pKEvent;
NTSTATUS status;
PLX_WAIT_OBJECT *pWaitObject;
// Setup driver access to the user event handle
status =
ObReferenceObjectByHandle(
(HANDLE)pEvent->hEvent, // Handle
EVENT_MODIFY_STATE, // Desired Access
*ExEventObjectType, // Object type
KernelMode, // Access Mode
&pKEvent, // Object pointer
NULL // Handle information
);
if (status != STATUS_SUCCESS)
return ApiInvalidHandle;
// Allocate a new wait object
pWaitObject =
ExAllocatePool(
NonPagedPool,
sizeof(PLX_WAIT_OBJECT)
);
if (pWaitObject == NULL)
{
DebugPrintf((
"ERROR - memory allocation for interrupt wait object failed\n"
));
// De-reference the object
ObDereferenceObject(
pKEvent
);
return ApiInsufficientResources;
}
// Record the wait object
pEvent->pWaitObject = (PLX_UINT_PTR)pWaitObject;
// Record the owner
pWaitObject->pOwner = pOwner;
// Store kernel event handle
pWaitObject->pKEvent = pKEvent;
// Clear interrupt source
pWaitObject->IntSource = INTR_TYPE_NONE;
// Set interrupt notification flags
PlxChipSetInterruptNotifyFlags(
pPlxIntr,
pWaitObject
);
// Add to list of waiting objects
ExInterlockedInsertTailList(
&(pdx->List_WaitObjects),
&(pWaitObject->ListEntry),
&(pdx->Lock_WaitObjectsList)
);
DebugPrintf((
"Registered interrupt wait object (%p)\n",
pWaitObject
));
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxNotificationWait
*
* Description: Put the process to sleep until wake-up event occurs or timeout
*
******************************************************************************/
RETURN_CODE
PlxNotificationWait(
DEVICE_EXTENSION *pdx,
PLX_NOTIFY_OBJECT *pEvent,
U32 Timeout_ms
)
{
// Implemented at API level in Windows driver
return ApiUnsupportedFunction;
}
/******************************************************************************
*
* Function : PlxNotificationCancel
*
* Description: Cancels a registered notification event
*
******************************************************************************/
RETURN_CODE
PlxNotificationCancel(
DEVICE_EXTENSION *pdx,
PLX_NOTIFY_OBJECT *pEvent,
VOID *pOwner
)
{
KIRQL IrqL_Original;
BOOLEAN bRemove;
PLIST_ENTRY pEntry;
PLX_WAIT_OBJECT *pWaitObject;
KeAcquireSpinLock(
&(pdx->Lock_WaitObjectsList),
&IrqL_Original
);
pEntry = pdx->List_WaitObjects.Flink;
// Traverse list to find the desired list object
while (pEntry != &(pdx->List_WaitObjects))
{
// Get the object
pWaitObject =
CONTAINING_RECORD(
pEntry,
PLX_WAIT_OBJECT,
ListEntry
);
// Default to not remove
bRemove = FALSE;
// Determine if object should be removed
if (pOwner == pWaitObject->pOwner)
{
if (pEvent == NULL)
{
bRemove = TRUE;
}
else if ((PLX_UINT_PTR)pWaitObject == pEvent->pWaitObject)
{
bRemove = TRUE;
}
}
// Remove object
if (bRemove)
{
DebugPrintf((
"Removing interrupt wait object (%p)...\n",
pWaitObject
));
// Remove the object from the list
RemoveEntryList(
pEntry
);
KeReleaseSpinLock(
&(pdx->Lock_WaitObjectsList),
IrqL_Original
);
// De-reference the kernel event object
ObDereferenceObject(
pWaitObject->pKEvent
);
// Release the list object
ExFreePool(
pWaitObject
);
// Return if removing only a specific object
if (pEvent != NULL)
return ApiSuccess;
// Reset to beginning of list
KeAcquireSpinLock(
&(pdx->Lock_WaitObjectsList),
&IrqL_Original
);
pEntry = pdx->List_WaitObjects.Flink;
}
else
{
// Jump to next item in the list
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pdx->Lock_WaitObjectsList),
IrqL_Original
);
return ApiFailed;
}
/******************************************************************************
*
* Function : PlxPciBusMemTransfer
*
* Description: Reads/Writes device memory to/from a buffer
*
******************************************************************************/
RETURN_CODE
PlxPciBusMemTransfer(
DEVICE_EXTENSION *pdx,
IOP_SPACE IopSpace,
U32 LocalAddress,
BOOLEAN bRemap,
U8 *pBuffer,
U32 ByteCount,
ACCESS_TYPE AccessType,
BOOLEAN bReadOperation
)
{
U8 BarIndex;
U16 Offset_RegRemap;
U32 RegValue;
U32 SpaceRange;
U32 SpaceOffset;
U32 RemapOriginal;
U32 BytesToTransfer;
PLX_UINT_PTR SpaceVa;
// Added to prevent compiler warnings
RemapOriginal = 0;
// Verify data alignment
switch (AccessType)
{
case BitSize8:
break;
case BitSize16:
if (LocalAddress & 0x1)
{
DebugPrintf(("ERROR - Local address not aligned\n"));
return ApiInvalidAddress;
}
if (ByteCount & 0x1)
{
DebugPrintf(("ERROR - Byte count not aligned\n"));
return ApiInvalidSize;
}
break;
case BitSize32:
if (LocalAddress & 0x3)
{
DebugPrintf(("ERROR - Local address not aligned\n"));
return ApiInvalidAddress;
}
if (ByteCount & 0x3)
{
DebugPrintf(("ERROR - Byte count not aligned\n"));
return ApiInvalidSize;
}
break;
default:
DebugPrintf(("ERROR - Invalid access type\n"));
return ApiInvalidAccessType;
}
// Get and Verify the Local Space
PlxChipGetSpace(
pdx,
IopSpace,
&BarIndex,
&Offset_RegRemap
);
if (BarIndex == (U8)-1)
{
return ApiInvalidIopSpace;
}
// Only memory spaces are supported by this function
if (pdx->PciBar[BarIndex].bIsIoSpace)
{
DebugPrintf(("ERROR - I/O spaces not supported by this function\n"));
return ApiInvalidIopSpace;
}
// Get kernel virtual address for the space
SpaceVa = (PLX_UINT_PTR)pdx->PciBar[BarIndex].pVa;
if (SpaceVa == 0)
{
DebugPrintf((
"ERROR - Invalid kernel VA (0x%08lx) for PCI BAR\n",
SpaceVa
));
return ApiInvalidAddress;
}
// Save the remap register
if (bRemap)
{
RemapOriginal =
PLX_REG_READ(
pdx,
Offset_RegRemap
);
}
else
{
// Make sure requested area doesn't exceed our local space window boundary
if ((LocalAddress + ByteCount) > pdx->PciBar[BarIndex].Size)
{
DebugPrintf(("ERROR - requested area exceeds space range\n"));
return ApiInvalidSize;
}
}
// Get the range of the space
SpaceRange = ~(pdx->PciBar[BarIndex].Size - 1);
// Transfer data in blocks
while (ByteCount != 0)
{
// Adjust remap if necessary
if (bRemap)
{
// Clear upper bits of remap
RegValue = RemapOriginal & ~SpaceRange;
// Adjust window to local address
RegValue |= LocalAddress & SpaceRange;
PLX_REG_WRITE(
pdx,
Offset_RegRemap,
RegValue
);
}
// Get current offset into space
SpaceOffset = LocalAddress & (~SpaceRange);
// Calculate bytes to transfer for next block
if (ByteCount <= (((~SpaceRange) + 1) - SpaceOffset))
{
BytesToTransfer = ByteCount;
}
else
{
BytesToTransfer = ((~SpaceRange) + 1) - SpaceOffset;
}
if (bReadOperation)
{
// Copy block to user buffer
switch (AccessType)
{
case BitSize8:
DEV_MEM_TO_USER_8(
pBuffer,
(SpaceVa + SpaceOffset),
BytesToTransfer
);
break;
case BitSize16:
DEV_MEM_TO_USER_16(
pBuffer,
(SpaceVa + SpaceOffset),
BytesToTransfer
);
break;
case BitSize32:
DEV_MEM_TO_USER_32(
pBuffer,
(SpaceVa + SpaceOffset),
BytesToTransfer
);
break;
case BitSize64:
// 64-bit not implemented yet
break;
}
}
else
{
// Copy user buffer to device memory
switch (AccessType)
{
case BitSize8:
USER_TO_DEV_MEM_8(
(SpaceVa + SpaceOffset),
pBuffer,
BytesToTransfer
);
break;
case BitSize16:
USER_TO_DEV_MEM_16(
(SpaceVa + SpaceOffset),
pBuffer,
BytesToTransfer
);
break;
case BitSize32:
USER_TO_DEV_MEM_32(
(SpaceVa + SpaceOffset),
pBuffer,
BytesToTransfer
);
break;
case BitSize64:
// 64-bit not implemented yet
break;
}
}
// Adjust for next block access
pBuffer += BytesToTransfer;
LocalAddress += BytesToTransfer;
ByteCount -= BytesToTransfer;
}
// Restore the remap register
if (bRemap)
{
PLX_REG_WRITE(
pdx,
Offset_RegRemap,
RemapOriginal
);
}
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxPciIoPortTransfer
*
* Description: Read or Write from/to an I/O port
*
******************************************************************************/
RETURN_CODE
PlxPciIoPortTransfer(
U64 IoPort,
ACCESS_TYPE AccessType,
VOID *pValue,
BOOLEAN bReadOperation
)
{
if (pValue == NULL)
return ApiNullParam;
if (bReadOperation)
{
switch (AccessType)
{
case BitSize8:
*(U8*)pValue =
IO_PORT_READ_8(
IoPort
);
break;
case BitSize16:
*(U16*)pValue =
IO_PORT_READ_16(
IoPort
);
break;
case BitSize32:
*(U32*)pValue =
IO_PORT_READ_32(
IoPort
);
break;
default:
return ApiInvalidAccessType;
}
}
else
{
switch (AccessType)
{
case BitSize8:
IO_PORT_WRITE_8(
IoPort,
*(U8*)pValue
);
break;
case BitSize16:
IO_PORT_WRITE_16(
IoPort,
*(U16*)pValue
);
break;
case BitSize32:
IO_PORT_WRITE_32(
IoPort,
*(U32*)pValue
);
break;
default:
return ApiInvalidAccessType;
}
}
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxPciPhysicalMemoryAllocate
*
* Description: Allocate physically contiguous page-locked memory
*
******************************************************************************/
RETURN_CODE
PlxPciPhysicalMemoryAllocate(
DEVICE_EXTENSION *pdx,
PLX_PHYSICAL_MEM *pPciMem,
BOOLEAN bSmallerOk,
VOID *pOwner
)
{
U32 DecrementAmount;
PLX_PHYS_MEM_OBJECT *pBufferObj;
// Initialize buffer information
pPciMem->UserAddr = 0;
pPciMem->PhysicalAddr = 0;
pPciMem->CpuPhysical = 0;
/*******************************************************
* Verify size
*
* A size of 0 is valid because this function may
* be called to allocate a common buffer of size 0;
* therefore, the information is reset & return sucess.
******************************************************/
if (pPciMem->Size == 0)
{
return ApiSuccess;
}
// Allocate memory for new list object
pBufferObj =
ExAllocatePool(
NonPagedPool,
sizeof(PLX_PHYS_MEM_OBJECT)
);
if (pBufferObj == NULL)
{
DebugPrintf((
"ERROR - Memory allocation for list object failed\n"
));
return ApiInsufficientResources;
}
// Set buffer request size
pBufferObj->Size = pPciMem->Size;
// Set Cacheability of the buffer
pBufferObj->bCacheable = Gbl_PhysicalMemoryCacheable;
// Setup amount to reduce on failure
DecrementAmount = (pPciMem->Size / 10);
DebugPrintf((
"Attempting to allocate physical memory (%d Kb)...\n",
(pPciMem->Size >> 10)
));
do
{
// Attempt to allocate the buffer
pBufferObj->pKernelVa =
Plx_dma_buffer_alloc(
pdx,
pBufferObj
);
if (pBufferObj->pKernelVa == NULL)
{
// Reduce memory request size if requested
if (bSmallerOk && (pBufferObj->Size > PAGE_SIZE))
{
pBufferObj->Size -= DecrementAmount;
}
else
{
// Release the list object
ExFreePool(
pBufferObj
);
DebugPrintf((
"ERROR - Physical memory allocation failed\n"
));
pPciMem->Size = 0;
return ApiInsufficientResources;
}
}
}
while (pBufferObj->pKernelVa == NULL);
// Record buffer owner
pBufferObj->pOwner = pOwner;
// Allocate MDL for buffer for later mapping into user space
pBufferObj->pMdl =
IoAllocateMdl(
pBufferObj->pKernelVa,
pBufferObj->Size,
FALSE, // Is this a secondary buffer?
FALSE, // Charge quota?
NULL // No IRP associated with MDL
);
// Check if the MDL allocation succeeded
if (pBufferObj->pMdl == NULL)
DebugPrintf(("ERROR - failed to allocate an MDL for buffer\n"));
else
{
// Build the MDL
MmBuildMdlForNonPagedPool(
pBufferObj->pMdl
);
}
// Initialize mappings list
InitializeListHead(
&(pBufferObj->List_Mappings)
);
KeInitializeSpinLock(
&(pBufferObj->Lock_MappingsList)
);
// Return buffer information
pPciMem->Size = pBufferObj->Size;
pPciMem->PhysicalAddr = pBufferObj->BusPhysical;
pPciMem->CpuPhysical = pBufferObj->CpuPhysical;
// Add buffer object to list
ExInterlockedInsertTailList(
&(pdx->List_PhysicalMem),
&(pBufferObj->ListEntry),
&(pdx->Lock_PhysicalMemList)
);
// Check if this is the common buffer allocation
if (pdx == pOwner)
{
// Store common buffer information
pGbl_CommonBuffer = pBufferObj;
}
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxPciPhysicalMemoryFree
*
* Description: Free previously allocated physically contiguous page-locked memory
*
******************************************************************************/
RETURN_CODE
PlxPciPhysicalMemoryFree(
DEVICE_EXTENSION *pdx,
PLX_PHYSICAL_MEM *pPciMem
)
{
KIRQL IrqL_Original;
PLIST_ENTRY pEntry;
PLX_PHYS_MEM_OBJECT *pBufferObj;
KeAcquireSpinLock(
&(pdx->Lock_PhysicalMemList),
&IrqL_Original
);
pEntry = pdx->List_PhysicalMem.Flink;
// Traverse list to find the desired list object
while (pEntry != &(pdx->List_PhysicalMem))
{
// Get the object
pBufferObj =
CONTAINING_RECORD(
pEntry,
PLX_PHYS_MEM_OBJECT,
ListEntry
);
// Check if the physical addresses matches
if (pBufferObj->BusPhysical == pPciMem->PhysicalAddr)
{
// Release lock until mappings are removed
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
// Make sure all mappings to this memory are unmapped
PlxPciPhysicalMemoryUnmapAll_ByOwner(
pdx,
pBufferObj,
NULL // Ignore owner to remove all mappings
);
// Remove the object from the list
KeAcquireSpinLock(
&(pdx->Lock_PhysicalMemList),
&IrqL_Original
);
RemoveEntryList(
pEntry
);
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
// Free the MDL
if (pBufferObj->pMdl != NULL)
{
IoFreeMdl(
pBufferObj->pMdl
);
}
Plx_dma_buffer_free(
pdx,
pBufferObj
);
// Release the list object
ExFreePool(
pBufferObj
);
// Check if this is the common buffer release
if (pGbl_CommonBuffer == pBufferObj)
{
// Clear common buffer information
pGbl_CommonBuffer = NULL;
}
return ApiSuccess;
}
// Jump to next item in the list
pEntry = pEntry->Flink;
}
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
DebugPrintf((
"ERROR - buffer object not found in list\n"
));
return ApiInvalidData;
}
/******************************************************************************
*
* Function : PlxPciPhysicalMemoryMap
*
* Description: Maps physical memory to User virtual address space
*
******************************************************************************/
RETURN_CODE
PlxPciPhysicalMemoryMap(
DEVICE_EXTENSION *pdx,
PLX_PHYSICAL_MEM *pPciMem,
BOOLEAN bDeviceMem,
VOID *pOwner
)
{
VOID *pUserVa;
KIRQL IrqL_Original;
BOOLEAN bFound;
PLIST_ENTRY pEntry;
PHYSICAL_ADDRESS AddrPhysical;
PLX_USER_MAPPING *pMapObject;
PLX_PHYS_MEM_OBJECT *pMemObject;
MEMORY_CACHING_TYPE CacheMode;
// Set default return value
pPciMem->UserAddr = 0;
pMemObject = NULL;
// Check if memory object is common buffer
if (pGbl_CommonBuffer != NULL)
{
if (pPciMem->PhysicalAddr == pGbl_CommonBuffer->BusPhysical)
{
pMemObject = pGbl_CommonBuffer;
}
}
// Find the memory object to map
if (pMemObject == NULL)
{
// Find the object in the list
KeAcquireSpinLock(
&(pdx->Lock_PhysicalMemList),
&IrqL_Original
);
pEntry = pdx->List_PhysicalMem.Flink;
bFound = FALSE;
// Traverse list to find the desired list object
while (!bFound && (pEntry != &(pdx->List_PhysicalMem)))
{
// Get the object
pMemObject =
CONTAINING_RECORD(
pEntry,
PLX_PHYS_MEM_OBJECT,
ListEntry
);
// Check if the physical addresses matches
if (pMemObject->BusPhysical == pPciMem->PhysicalAddr)
{
bFound = TRUE;
}
else
{
// Jump to next item in the list
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
if (!bFound)
{
DebugPrintf((
"ERROR - Physical memory object not found in list, unable to map\n"
));
return ApiInvalidAddress;
}
}
// Verify an MDL for the memory
if (pMemObject->pMdl == NULL)
{
DebugPrintf((
"ERROR - MDL does not exist for this memory, cannot map to user space\n"
));
return ApiInsufficientResources;
}
// Set cache mode
if (Gbl_PhysicalMemoryCacheable)
{
CacheMode = MmCached;
}
else
{
CacheMode = MmNonCached;
}
// Attempt to map the region
pUserVa =
MmMapLockedPagesSpecifyCache(
pMemObject->pMdl,
UserMode,
CacheMode,
NULL,
FALSE,
NormalPagePriority
);
if (pUserVa == NULL)
{
DebugPrintf((
"ERROR - Unable to map Physical address (0x%08lx) ==> User Space\n",
pMemObject->CpuPhysical
));
return ApiInsufficientResources;
}
DebugPrintf((
"Mapped Physical address (0x%08lx) ==> Virtual address (0x%p) (owner=%p)\n",
pMemObject->CpuPhysical, pUserVa, pOwner
));
// Return virtual address
pPciMem->UserAddr = (PLX_UINT_PTR)pUserVa;
// Save mapping to list for this physical memory
pMapObject =
ExAllocatePool(
NonPagedPool,
sizeof(PLX_USER_MAPPING)
);
if (pMapObject == NULL)
{
DebugPrintf((
"ERROR: Unable to save mapping, insufficient memory\n"
));
}
else
{
// Clear the virtual addresses
RtlZeroMemory(
pMapObject->pUserVa,
MAX_VIRTUAL_ADDR * sizeof(VOID*)
);
// Record the IRP owner
pMapObject->pOwner = pOwner;
// Record the virtual address
pMapObject->pUserVa[0] = pUserVa;
// Save the mapping for later cleanup
ExInterlockedInsertTailList(
&(pMemObject->List_Mappings),
&(pMapObject->ListEntry),
&(pMemObject->Lock_MappingsList)
);
}
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxPciPhysicalMemoryUnmap
*
* Description: Unmap physical memory from User virtual address space
*
******************************************************************************/
RETURN_CODE
PlxPciPhysicalMemoryUnmap(
DEVICE_EXTENSION *pdx,
PLX_PHYSICAL_MEM *pPciMem,
VOID *pOwner
)
{
VOID *pUserVa;
KIRQL IrqL_Original;
BOOLEAN bFound;
PLIST_ENTRY pEntry;
PHYSICAL_ADDRESS AddrPhysical;
PLX_USER_MAPPING *pMapObject;
PLX_PHYS_MEM_OBJECT *pMemObject;
pMemObject = NULL;
// Check if memory object is common buffer
if (pGbl_CommonBuffer != NULL)
{
if (pPciMem->PhysicalAddr == pGbl_CommonBuffer->BusPhysical)
{
pMemObject = pGbl_CommonBuffer;
}
}
// Find the memory object to unmap
if (pMemObject == NULL)
{
// Find the object in the list
KeAcquireSpinLock(
&(pdx->Lock_PhysicalMemList),
&IrqL_Original
);
pEntry = pdx->List_PhysicalMem.Flink;
bFound = FALSE;
// Traverse list to find the desired list object
while (!bFound && (pEntry != &(pdx->List_PhysicalMem)))
{
// Get the object
pMemObject =
CONTAINING_RECORD(
pEntry,
PLX_PHYS_MEM_OBJECT,
ListEntry
);
// Check if the physical addresses matches
if (pMemObject->BusPhysical == pPciMem->PhysicalAddr)
{
bFound = TRUE;
}
else
{
// Jump to next item in the list
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pdx->Lock_PhysicalMemList),
IrqL_Original
);
if (!bFound)
{
DebugPrintf((
"ERROR - Physical memory object not found in list, unable to unmap\n"
));
return ApiInvalidAddress;
}
}
// Find the map object to unmap
KeAcquireSpinLock(
&(pMemObject->Lock_MappingsList),
&IrqL_Original
);
pEntry = pMemObject->List_Mappings.Flink;
bFound = FALSE;
// Traverse list to find the desired list object
while (!bFound && (pEntry != &(pMemObject->List_Mappings)))
{
// Get the object
pMapObject =
CONTAINING_RECORD(
pEntry,
PLX_USER_MAPPING,
ListEntry
);
// Compare owner & virtual address
if ((pOwner == pMapObject->pOwner) &&
((PLX_UINT_PTR)pMapObject->pUserVa[0] == pPciMem->UserAddr))
{
// Remove entry from the list
RemoveEntryList(
pEntry
);
bFound = TRUE;
}
else
{
// Jump to next item in the list
pEntry = pEntry->Flink;
}
}
KeReleaseSpinLock(
&(pMemObject->Lock_MappingsList),
IrqL_Original
);
if (!bFound)
{
DebugPrintf((
"ERROR - Mapping object not found in list, unable to unmap\n"
));
return ApiInvalidAddress;
}
// Unmap the memory
if (pMapObject->pUserVa[0] != NULL)
{
DebugPrintf((
"Unmapping Virtual address (0x%p) from User Space (owner=%p)...\n",
pMapObject->pUserVa[0], pOwner
));
MmUnmapLockedPages(
pMapObject->pUserVa[0],
pMemObject->pMdl
);
// Clear virtual address
pPciMem->UserAddr = 0;
}
else
{
DebugPrintf((
"ERROR - Invalid virtual address (0x%p), cannot unmap\n",
pMapObject->pUserVa[0]
));
return ApiInvalidAddress;
}
// Release memory for map object
ExFreePool(
pMapObject
);
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxPciBarMap
*
* Description: Map a PCI BAR Space into User virtual space
*
******************************************************************************/
RETURN_CODE
PlxPciBarMap(
DEVICE_EXTENSION *pdx,
U8 BarIndex,
VOID *pUserVa,
VOID *pOwner
)
{
U8 i;
PMDL pMdl[MAX_VIRTUAL_ADDR];
VOID *Va[MAX_VIRTUAL_ADDR];
VOID *VaBase;
BOOLEAN bMapError;
PLX_USER_MAPPING *pMapObject;
// Set default address
*(PLX_UINT_PTR*)pUserVa = 0;
// Verify BAR is of type memory
if (pdx->PciBar[BarIndex].bIsIoSpace)
{
DebugPrintf((
"ERROR - BAR %d is an I/O space, cannot map to user space\n",
BarIndex
));
return ApiInvalidPciSpace;
}
// Check if the space is valid
if (pdx->PciBar[BarIndex].Physical.QuadPart == 0)
{
DebugPrintf((
"ERROR - BAR %d address (0x%08lx) is invalid\n",
BarIndex, pdx->PciBar[BarIndex].Physical
));
return ApiInvalidAddress;
}
// Verify Kernel virtual address
if (pdx->PciBar[BarIndex].pVa == NULL)
{
DebugPrintf((
"ERROR - Kernel Virtual address for BAR %d is not valid\n",
BarIndex
));
return ApiInvalidAddress;
}
// Make sure memory size is valid
if (pdx->PciBar[BarIndex].Size == 0)
{
DebugPrintf((
"ERROR - Size of BAR %d is 0\n",
BarIndex
));
return ApiInvalidSize;
}
// Verify the MDL
if (pdx->PciBar[BarIndex].pMdl == NULL)
{
DebugPrintf((
"ERROR - MDL does not exist for BAR %d\n",
BarIndex
));
return ApiInsufficientResources;
}
// Make sure there is enough space to store all virtual addresses
if (pdx->PciBar[BarIndex].Size > (MAX_VIRTUAL_ADDR * MAX_MDL_SIZE))
{
DebugPrintf((
"ERROR - Not enough virtual addresses for space of size %dMB\n",
(pdx->PciBar[BarIndex].Size >> 20)
));
return ApiInsufficientResources;
}
// Set initial values
i = 0;
pMdl[0] = pdx->PciBar[BarIndex].pMdl;
VaBase = NULL;
bMapError = FALSE;
// Clear the virtual addresses
RtlZeroMemory(
Va,
MAX_VIRTUAL_ADDR * sizeof(VOID*)
);
// Attempt to map the BAR into user space
while ((i < MAX_VIRTUAL_ADDR) && (pMdl[i] != NULL))
{
Va[i] =
MmMapLockedPagesSpecifyCache(
pMdl[i],
UserMode,
MmNonCached, // Do not cache PCI BAR space
VaBase, // User address to use
FALSE,
LowPagePriority
);
// Check if the mapping succeeded
if (Va[i] == NULL)
{
DebugPrintf((
"ERROR - Unable to map PCI BAR %d (0x%08lx) ==> User space\n",
BarIndex, pdx->PciBar[BarIndex].Physical
));
bMapError = TRUE;
}
else if (VaBase != NULL)
{
// Make sure VA address is consecutive
if (VaBase != Va[i])
{
DebugPrintf((
"ERROR - Unable to assign contiguous virtual addresses for PCI BAR %d (0x%08lx) ==> User space\n",
BarIndex, pdx->PciBar[BarIndex].Physical
));
bMapError = TRUE;
}
}
if (bMapError)
{
// Release any previous mappings
while (i != 0)
{
i--;
MmUnmapLockedPages(
Va[i],
pMdl[i]
);
}
return ApiInsufficientResources;
}
// Adjust to next consecutive base address
VaBase = ((U8*)Va[i]) + MmGetMdlByteCount(pMdl[i]);
// Go to next entry
i++;
pMdl[i] = pMdl[i-1]->Next;
}
// Return initial base address
*(PLX_UINT_PTR*)pUserVa = (PLX_UINT_PTR)Va[0];
DebugPrintf((
"Mapped PCI BAR %d (0x%08lx) ==> User VA (0x%08lx) (owner=%p)\n",
BarIndex, pdx->PciBar[BarIndex].Physical, *(PLX_UINT_PTR*)pUserVa, pOwner
));
// Add mapping to list for later unmapping
pMapObject =
ExAllocatePool(
NonPagedPool,
sizeof(PLX_USER_MAPPING)
);
if (pMapObject == NULL)
{
DebugPrintf(("ERROR: Unable to save mapping, insufficient memory\n"));
}
else
{
// Record mapping properties
pMapObject->pOwner = pOwner;
pMapObject->BarIndex = BarIndex;
RtlCopyMemory(
pMapObject->pUserVa,
Va,
MAX_VIRTUAL_ADDR * sizeof(VOID*)
);
// Save the mapping for later cleanup
ExInterlockedInsertTailList(
&(pdx->List_BarMappings),
&(pMapObject->ListEntry),
&(pdx->Lock_BarMappingsList)
);
}
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxPciBarUnmap
*
* Description: Unmap a previously mapped PCI BAR Space from User virtual space
*
******************************************************************************/
RETURN_CODE
PlxPciBarUnmap(
DEVICE_EXTENSION *pdx,
VOID *UserVa,
VOID *pOwner
)
{
U8 i;
PMDL pMdl;
KIRQL IrqL_Original;
PLIST_ENTRY pEntry;
PLX_USER_MAPPING *pMapObject;
// Find the previous mapping object
KeAcquireSpinLock(
&(pdx->Lock_BarMappingsList),
&IrqL_Original
);
pEntry = pdx->List_BarMappings.Flink;
while (pEntry != &(pdx->List_BarMappings))
{
pMapObject =
CONTAINING_RECORD(
pEntry,
PLX_USER_MAPPING,
ListEntry
);
if ((pMapObject->pOwner == pOwner) &&
(pMapObject->pUserVa[0] == UserVa))
{
// Remove map object from list
RemoveEntryList(
pEntry
);
KeReleaseSpinLock(
&(pdx->Lock_BarMappingsList),
IrqL_Original
);
DebugPrintf((
"Unmapping Virtual address (0x%p) for BAR %d from User Space (owner=%p)...\n",
pMapObject->pUserVa[0], pMapObject->BarIndex, pOwner
));
// Unmap the space
i = 0;
pMdl = pdx->PciBar[pMapObject->BarIndex].pMdl;
while ((i < MAX_VIRTUAL_ADDR) && (pMapObject->pUserVa[i] != NULL))
{
MmUnmapLockedPages(
pMapObject->pUserVa[i],
pMdl
);
i++;
pMdl = pMdl->Next;
}
// Release the list object
ExFreePool(
pMapObject
);
return ApiSuccess;
}
// Jump to next item
pEntry = pEntry->Flink;
}
DebugPrintf((
"ERROR - Map object not found in list...\n"
));
KeReleaseSpinLock(
&(pdx->Lock_BarMappingsList),
IrqL_Original
);
return ApiInvalidAddress;
}