www.pudn.com > PlxSdk.rar > ApiFunctions.c
/*******************************************************************************
* Copyright (c) 2007 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:
*
* ApiFunctions.c
*
* Description:
*
* Implements the PLX API functions
*
* Revision History:
*
* 06-01-07 : PLX SDK v5.10
*
******************************************************************************/
#include "ApiFunctions.h"
#include "Eep_6000.h"
#include "PciSupport.h"
#include "PlxInterrupt.h"
#include "SupportFunc.h"
/*******************************************************************************
*
* Function : PlxDeviceFind
*
* Description: Search for a specific device using device key parameters
*
******************************************************************************/
RETURN_CODE
PlxDeviceFind(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U8 *pDeviceNumber
)
{
U8 DeviceCount;
BOOLEAN bMatchId;
BOOLEAN bMatchLoc;
DEVICE_OBJECT *fdo;
DeviceCount = 0;
// Get first device instance in list
fdo = pdx->pDeviceObject->DriverObject->DeviceObject;
// Compare with items in device list
while (fdo != NULL)
{
// Get the device extension
pdx = fdo->DeviceExtension;
// Assume successful match
bMatchLoc = TRUE;
bMatchId = TRUE;
//
// Compare device key information
//
// Compare Bus number
if (pKey->bus != (U8)PCI_FIELD_IGNORE)
{
if (pKey->bus != pdx->Key.bus)
{
bMatchLoc = FALSE;
}
}
// Compare Slot number
if (pKey->slot != (U8)PCI_FIELD_IGNORE)
{
if (pKey->slot != pdx->Key.slot)
{
bMatchLoc = FALSE;
}
}
// Compare Function number
if (pKey->function != (U8)PCI_FIELD_IGNORE)
{
if (pKey->function != pdx->Key.function)
{
bMatchLoc = FALSE;
}
}
//
// Compare device ID information
//
// Compare Vendor ID
if (pKey->VendorId != (U16)PCI_FIELD_IGNORE)
{
if (pKey->VendorId != pdx->Key.VendorId)
{
bMatchId = FALSE;
}
}
// Compare Device ID
if (pKey->DeviceId != (U16)PCI_FIELD_IGNORE)
{
if (pKey->DeviceId != pdx->Key.DeviceId)
{
bMatchId = FALSE;
}
}
// Compare Subsystem Vendor ID
if (pKey->SubVendorId != (U16)PCI_FIELD_IGNORE)
{
if (pKey->SubVendorId != pdx->Key.SubVendorId)
{
bMatchId = FALSE;
}
}
// Compare Subsystem Device ID
if (pKey->SubDeviceId != (U16)PCI_FIELD_IGNORE)
{
if (pKey->SubDeviceId != pdx->Key.SubDeviceId)
{
bMatchId = FALSE;
}
}
// Compare Revision
if (pKey->Revision != (U8)PCI_FIELD_IGNORE)
{
if (pKey->Revision != pdx->Key.Revision)
{
bMatchId = FALSE;
}
}
// Check if match on location and ID
if (bMatchLoc && bMatchId)
{
// Match found, check if it is the desired device
if (DeviceCount == *pDeviceNumber)
{
// Copy the device information
*pKey = pdx->Key;
DebugPrintf((
"Criteria matched device %04X_%04X [b:%02x s:%02x f:%02x]\n",
pdx->Key.DeviceId, pdx->Key.VendorId,
pdx->Key.bus, pdx->Key.slot, pdx->Key.function
));
return ApiSuccess;
}
// Increment device count
DeviceCount++;
}
// Jump to next entry
fdo = fdo->NextDevice;
}
// Return number of matched devices
*pDeviceNumber = DeviceCount;
DebugPrintf(("Criteria did not match any devices\n"));
return ApiInvalidDeviceInfo;
}
/*******************************************************************************
*
* Function : PlxChipTypeGet
*
* Description: Returns PLX chip type and revision
*
******************************************************************************/
RETURN_CODE
PlxChipTypeGet(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 *pChipType,
U8 *pRevision
)
{
U32 DevVenId;
// Reset values
*pChipType = 0;
*pRevision = pKey->Revision;
DevVenId = ((U32)pKey->DeviceId << 16) | pKey->VendorId;
switch (DevVenId)
{
case 0x00213388: // 6254 - NT mode
if (pKey->Revision == 0x4)
{
*pChipType = 0x6254;
*pRevision = 0xBB;
}
break;
case 0x00293388: // 6540 - NT mode
case 0x654110B5: // 6540 - NT mode
case 0x654210B5: // 6540 - NT mode (Secondary side)
*pChipType = 0x6540;
if (pKey->Revision == 0x2)
*pRevision = 0xBB;
break;
default:
DebugPrintf(("ERROR - Unable to determine chip type\n"));
return ApiInvalidDeviceInfo;
}
DebugPrintf((
"Device %04X_%04X = %04X rev %02X\n",
pKey->DeviceId, pKey->VendorId, *pChipType, *pRevision
));
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxChipTypeSet
*
* Description: Sets the PLX chip type dynamically
*
******************************************************************************/
RETURN_CODE
PlxChipTypeSet(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 ChipType,
U8 Revision
)
{
// Setting the PLX chip type is not supported in this PnP driver
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxGetPortProperties
*
* Description: Returns the current port information and status
*
******************************************************************************/
RETURN_CODE
PlxGetPortProperties(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
PLX_PORT_PROP *pPortProp
)
{
DebugPrintf(("Device does not support PCI Express Capability\n"));
// Set default value for properties
RtlZeroMemory(pPortProp, sizeof(PLX_PORT_PROP));
pPortProp->PortType = PLX_PORT_ENDPOINT;
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxPciBoardReset
*
* Description: Resets a device using software reset feature of PLX chip
*
******************************************************************************/
RETURN_CODE
PlxPciBoardReset(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey
)
{
// Device reset not implemented
return ApiUnsupportedFunction;
}
/******************************************************************************
*
* Function : PlxRegisterRead
*
* Description: Reads a PLX-specific control register
*
*****************************************************************************/
U32
PlxRegisterRead(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 offset,
RETURN_CODE *pReturnCode,
BOOLEAN bAdjustForPort
)
{
// 6000 series devices do not have PLX-specific local registers
*pReturnCode = ApiUnsupportedFunction;
return 0;
}
/******************************************************************************
*
* Function : PlxRegisterWrite
*
* Description: Writes to a PLX-specific control register
*
*****************************************************************************/
RETURN_CODE
PlxRegisterWrite(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 offset,
U32 value,
BOOLEAN bAdjustForPort
)
{
// 6000 series devices do not have PLX-specific local registers
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxPciBarProperties
*
* Description: Returns the properties of a PCI BAR space
*
******************************************************************************/
RETURN_CODE
PlxPciBarProperties(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U8 BarIndex,
PLX_PCI_BAR_PROP *pBarProperties
)
{
// Verify BAR number
switch (BarIndex)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
break;
default:
return ApiInvalidIndex;
}
// Get the actual BAR value in case OS didn't provide it
if ((pdx->PciBar[BarIndex].Properties.BarValue == 0) &&
(pdx->PciBar[BarIndex].Properties.Size != 0))
{
PLX_PCI_REG_READ(
pdx,
0x10 + (BarIndex * sizeof(U32)),
&pdx->PciBar[BarIndex].Properties.BarValue
);
}
// Return PCI BAR properties
*pBarProperties = pdx->PciBar[BarIndex].Properties;
// Display BAR properties
DebugPrintf((
" PCI BAR %d: %08X\n",
BarIndex, pdx->PciBar[BarIndex].Properties.BarValue
));
DebugPrintf((
" Phys Addr: %08lX\n",
(PLX_UINT_PTR)pdx->PciBar[BarIndex].Properties.Physical
));
if (pdx->PciBar[BarIndex].Properties.Size >= (1 << 10))
{
DebugPrintf((
" Size : %08lx (%ld Kb)\n",
(PLX_UINT_PTR)pdx->PciBar[BarIndex].Properties.Size,
(PLX_UINT_PTR)pdx->PciBar[BarIndex].Properties.Size >> 10
));
}
else
{
DebugPrintf((
" Size : %08lx (%ld bytes)\n",
(PLX_UINT_PTR)pdx->PciBar[BarIndex].Properties.Size,
(PLX_UINT_PTR)pdx->PciBar[BarIndex].Properties.Size
));
}
DebugPrintf((
" Prefetch?: %s\n",
(pdx->PciBar[BarIndex].Properties.bPrefetchable) ? "Yes" : "No"
));
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxPciBarMap
*
* Description: Map a PCI BAR Space into User virtual space
*
******************************************************************************/
RETURN_CODE
PlxPciBarMap(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U8 BarIndex,
VOID *pUserVa,
VOID *pOwner
)
{
// Handled in Dispatch_mmap() in Linux
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxPciBarUnmap
*
* Description: Unmap a previously mapped PCI BAR Space from User virtual space
*
******************************************************************************/
RETURN_CODE
PlxPciBarUnmap(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
VOID *UserVa,
VOID *pOwner
)
{
// Handled at user API level in Linux
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxEepromPresent
*
* Description: Returns the state of the EEPROM as reported by the PLX device
*
******************************************************************************/
RETURN_CODE
PlxEepromPresent(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
PLX_EEPROM_STATUS *pStatus
)
{
BOOLEAN bFlag;
/**************************************************************
* Since PCI 6000 devices do not report whether an EEPROM
* is present or not, the driver manually probes for it.
*************************************************************/
// Probe for EEPROM
PlxEepromProbe(
pdx,
pKey,
&bFlag
);
if (bFlag)
{
*pStatus = PLX_EEPROM_STATUS_VALID;
}
else
{
*pStatus = PLX_EEPROM_STATUS_NONE;
}
return ApiSuccess;
}
/******************************************************************************
*
* Function : PlxEepromProbe
*
* Description: Probes for the presence of an EEPROM
*
*****************************************************************************/
RETURN_CODE
PlxEepromProbe(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
BOOLEAN *pFlag
)
{
U16 OffsetProbe;
U16 ValueRead;
U16 ValueWrite;
U16 ValueOriginal;
RETURN_CODE rc;
// Default to no EEPROM present
*pFlag = FALSE;
// Set EEPROM offset to use for probe
OffsetProbe = EEPROM_6000_TEST_OFFSET;
DebugPrintf(("Probing EEPROM at offset %02xh\n", OffsetProbe));
// Get the current value
rc =
PlxEepromReadByOffset_16(
pdx,
pKey,
OffsetProbe,
&ValueOriginal
);
if (rc != ApiSuccess)
return rc;
// Prepare inverse value to write
ValueWrite = (U16)~ValueOriginal;
// Write inverse of original value
rc =
PlxEepromWriteByOffset_16(
pdx,
pKey,
OffsetProbe,
ValueWrite
);
if (rc != ApiSuccess)
return rc;
// Read updated value
rc =
PlxEepromReadByOffset_16(
pdx,
pKey,
OffsetProbe,
&ValueRead
);
if (rc != ApiSuccess)
return rc;
// Check if value was written properly
if (ValueRead == ValueWrite)
{
DebugPrintf(("Probe detected an EEPROM present\n"));
*pFlag = TRUE;
// Restore the original value
PlxEepromWriteByOffset_16(
pdx,
pKey,
OffsetProbe,
ValueOriginal
);
}
else
{
DebugPrintf(("Probe did not detect an EEPROM\n"));
}
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxEepromCrcGet
*
* Description: Returns the EEPROM CRC and its status
*
******************************************************************************/
RETURN_CODE
PlxEepromCrcGet(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 *pCrc,
U8 *pCrcStatus
)
{
// For 6000-series, treat EEPROM signature like CRC
return Plx6000_EepromSignatureGet(
pdx,
pCrc,
pCrcStatus
);
}
/*******************************************************************************
*
* Function : PlxEepromCrcUpdate
*
* Description: Updates the EEPROM CRC
*
******************************************************************************/
RETURN_CODE
PlxEepromCrcUpdate(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 *pCrc,
BOOLEAN bUpdateEeprom
)
{
// Clear return value
*pCrc = 0;
// Device does not support CRC
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxEepromReadByOffset
*
* Description: Read a 32-bit value from the EEPROM at a specified offset
*
******************************************************************************/
RETURN_CODE
PlxEepromReadByOffset(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U16 offset,
U32 *pValue
)
{
// Make sure offset is aligned on 32-bit boundary
if (offset & (3 << 0))
{
*pValue = 0;
return ApiInvalidOffset;
}
Plx6000_EepromReadByOffset_16(
pdx,
offset,
(U16*)pValue
);
Plx6000_EepromReadByOffset_16(
pdx,
offset + sizeof(U16),
(U16*)((U8*)pValue + sizeof(U16))
);
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxEepromWriteByOffset
*
* Description: Write a 32-bit value to the EEPROM at a specified offset
*
******************************************************************************/
RETURN_CODE
PlxEepromWriteByOffset(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U16 offset,
U32 value
)
{
// Make sure offset is aligned on 32-bit boundary
if (offset & (3 << 0))
{
return ApiInvalidOffset;
}
Plx6000_EepromWriteByOffset_16(
pdx,
offset,
(U16)value
);
Plx6000_EepromWriteByOffset_16(
pdx,
offset + sizeof(U16),
(U16)(value >> 16)
);
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxEepromReadByOffset_16
*
* Description: Read a 16-bit value from the EEPROM at a specified offset
*
******************************************************************************/
RETURN_CODE
PlxEepromReadByOffset_16(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U16 offset,
U16 *pValue
)
{
// Make sure offset is aligned on 16-bit boundary
if (offset & (1 << 0))
{
*pValue = 0;
return ApiInvalidOffset;
}
return Plx6000_EepromReadByOffset_16(
pdx,
offset,
pValue
);
}
/*******************************************************************************
*
* Function : PlxEepromWriteByOffset_16
*
* Description: Write a 16-bit value to the EEPROM at a specified offset
*
******************************************************************************/
RETURN_CODE
PlxEepromWriteByOffset_16(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U16 offset,
U16 value
)
{
// Make sure offset is aligned on 16-bit boundary
if (offset & (1 << 0))
{
return ApiInvalidOffset;
}
return Plx6000_EepromWriteByOffset_16(
pdx,
offset,
value
);
}
/*******************************************************************************
*
* Function : PlxPciIoPortTransfer
*
* Description: Read or Write from/to an I/O port
*
******************************************************************************/
RETURN_CODE
PlxPciIoPortTransfer(
U64 IoPort,
VOID *pBuffer,
U32 SizeInBytes,
PLX_ACCESS_TYPE AccessType,
BOOLEAN bReadOperation
)
{
if (pBuffer == NULL)
return ApiNullParam;
// Verify size & type
switch (AccessType)
{
case BitSize8:
break;
case BitSize16:
if (IoPort & (1 << 0))
{
DebugPrintf(("ERROR - I/O port not aligned on 16-bit boundary\n"));
return ApiInvalidAddress;
}
if (SizeInBytes & (1 << 0))
{
DebugPrintf(("ERROR - Byte count not aligned on 16-bit boundary\n"));
return ApiInvalidSize;
}
break;
case BitSize32:
if (IoPort & 0x3)
{
DebugPrintf(("ERROR - I/O port not aligned on 32-bit boundary\n"));
return ApiInvalidAddress;
}
if (SizeInBytes & 0x3)
{
DebugPrintf(("ERROR - Byte count not aligned on 32-bit boundary\n"));
return ApiInvalidSize;
}
break;
default:
return ApiInvalidAccessType;
}
if (bReadOperation)
{
switch (AccessType)
{
case BitSize8:
IO_PORT_READ_8_BUFFER(
IoPort,
pBuffer,
SizeInBytes
);
break;
case BitSize16:
IO_PORT_READ_16_BUFFER(
IoPort,
pBuffer,
SizeInBytes
);
break;
case BitSize32:
IO_PORT_READ_32_BUFFER(
IoPort,
pBuffer,
SizeInBytes
);
break;
default:
// Added to avoid compiler warnings
break;
}
}
else
{
switch (AccessType)
{
case BitSize8:
IO_PORT_WRITE_8_BUFFER(
IoPort,
pBuffer,
SizeInBytes
);
break;
case BitSize16:
IO_PORT_WRITE_16_BUFFER(
IoPort,
pBuffer,
SizeInBytes
);
break;
case BitSize32:
IO_PORT_WRITE_32_BUFFER(
IoPort,
pBuffer,
SizeInBytes
);
break;
default:
// Added to avoid compiler warnings
break;
}
}
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 =
kmalloc(
sizeof(PLX_PHYS_MEM_OBJECT),
GFP_KERNEL
);
if (pBufferObj == NULL)
{
DebugPrintf((
"ERROR - Memory allocation for list object failed\n"
));
return ApiInsufficientResources;
}
// Set buffer request size
pBufferObj->Size = pPciMem->Size;
// 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
kfree(
pBufferObj
);
DebugPrintf((
"ERROR - Physical memory allocation failed\n"
));
pPciMem->Size = 0;
return ApiInsufficientResources;
}
}
}
while (pBufferObj->pKernelVa == NULL);
// Record buffer owner
pBufferObj->pOwner = pOwner;
// Assign buffer to device if provided
if (pOwner != pGbl_DriverObject)
{
// Return buffer information
pPciMem->Size = pBufferObj->Size;
pPciMem->PhysicalAddr = pBufferObj->BusPhysical;
pPciMem->CpuPhysical = pBufferObj->CpuPhysical;
// Add buffer object to list
spin_lock(
&(pdx->Lock_PhysicalMemList)
);
list_add_tail(
&(pBufferObj->ListEntry),
&(pdx->List_PhysicalMem)
);
spin_unlock(
&(pdx->Lock_PhysicalMemList)
);
}
else
{
// Store common buffer information
pGbl_DriverObject->CommonBuffer = *pBufferObj;
// Release the list object
kfree(
pBufferObj
);
}
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxPciPhysicalMemoryFree
*
* Description: Free previously allocated physically contiguous page-locked memory
*
******************************************************************************/
RETURN_CODE
PlxPciPhysicalMemoryFree(
DEVICE_EXTENSION *pdx,
PLX_PHYSICAL_MEM *pPciMem
)
{
struct list_head *pEntry;
PLX_PHYS_MEM_OBJECT *pBufferObj;
spin_lock(
&(pdx->Lock_PhysicalMemList)
);
pEntry = pdx->List_PhysicalMem.next;
// Traverse list to find the desired list object
while (pEntry != &(pdx->List_PhysicalMem))
{
// Get the object
pBufferObj =
list_entry(
pEntry,
PLX_PHYS_MEM_OBJECT,
ListEntry
);
// Check if the physical addresses matches
if (pBufferObj->BusPhysical == pPciMem->PhysicalAddr)
{
// Remove the object from the list
list_del(
pEntry
);
spin_unlock(
&(pdx->Lock_PhysicalMemList)
);
// Release the buffer
Plx_dma_buffer_free(
pdx,
pBufferObj
);
// Release the list object
kfree(
pBufferObj
);
return ApiSuccess;
}
// Jump to next item in the list
pEntry = pEntry->next;
}
spin_unlock(
&(pdx->Lock_PhysicalMemList)
);
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,
VOID *pOwner
)
{
// Handled in Dispatch_mmap() in Linux
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxPciPhysicalMemoryUnmap
*
* Description: Unmap physical memory from User virtual address space
*
******************************************************************************/
RETURN_CODE
PlxPciPhysicalMemoryUnmap(
DEVICE_EXTENSION *pdx,
PLX_PHYSICAL_MEM *pPciMem,
VOID *pOwner
)
{
// Handled by user-level API in Linux
return ApiUnsupportedFunction;
}
/******************************************************************************
*
* Function : PlxInterruptEnable
*
* Description: Enables specific interupts of the PLX Chip
*
******************************************************************************/
RETURN_CODE
PlxInterruptEnable(
DEVICE_EXTENSION *pdx,
PLX_INTERRUPT *pPlxIntr
)
{
// DBG - Not implemented yet
return ApiUnsupportedFunction;
}
/******************************************************************************
*
* Function : PlxInterruptDisable
*
* Description: Disables specific interrupts of the PLX Chip
*
******************************************************************************/
RETURN_CODE
PlxInterruptDisable(
DEVICE_EXTENSION *pdx,
PLX_INTERRUPT *pPlxIntr
)
{
// DBG - Not implemented yet
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxNotificationRegisterFor
*
* Description: Registers a wait object for notification on interrupt(s)
*
******************************************************************************/
RETURN_CODE
PlxNotificationRegisterFor(
DEVICE_EXTENSION *pdx,
PLX_INTERRUPT *pPlxIntr,
VOID **pUserWaitObject,
VOID *pOwner
)
{
unsigned long flags;
PLX_WAIT_OBJECT *pWaitObject;
// Allocate a new wait object
pWaitObject =
kmalloc(
sizeof(PLX_WAIT_OBJECT),
GFP_KERNEL
);
if (pWaitObject == NULL)
{
DebugPrintf((
"ERROR - memory allocation for interrupt wait object failed\n"
));
return ApiInsufficientResources;
}
// Provide the wait object to the user app
*pUserWaitObject = pWaitObject;
// Record the owner
pWaitObject->pOwner = pOwner;
// Clear interrupt source
pWaitObject->Source_Ints = INTR_TYPE_NONE;
pWaitObject->Source_Doorbell = 0;
// Mark the object as pending
pWaitObject->bPending = TRUE;
// Initialize wait queue
init_waitqueue_head(
&(pWaitObject->WaitQueue)
);
// Set interrupt notification flags
pWaitObject->Notify_Flags = INTR_TYPE_NONE;
pWaitObject->Notify_Doorbell = pPlxIntr->Doorbell;
if (pPlxIntr->Message)
pWaitObject->Notify_Flags |= INTR_TYPE_MESSAGE;
if (pPlxIntr->SecResetDeassert)
pWaitObject->Notify_Flags |= INTR_TYPE_S_RSTIN;
if (pPlxIntr->SecPmeDeassert)
pWaitObject->Notify_Flags |= INTR_TYPE_S_PME;
if (pPlxIntr->GPIO14)
pWaitObject->Notify_Flags |= INTR_TYPE_GPIO14;
if (pPlxIntr->GPIO4)
pWaitObject->Notify_Flags |= INTR_TYPE_GPIO4;
// Add to list of waiting objects
spin_lock_irqsave(
&(pdx->Lock_WaitObjectsList),
flags
);
list_add_tail(
&(pWaitObject->ListEntry),
&(pdx->List_WaitObjects)
);
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
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,
VOID *pUserWaitObject,
PLX_UINT_PTR Timeout_ms
)
{
int Wait_rc;
RETURN_CODE rc;
PLX_UINT_PTR Timeout_sec;
unsigned long flags;
struct list_head *pEntry;
PLX_WAIT_OBJECT *pWaitObject;
// Find the wait object in the list
spin_lock_irqsave(
&(pdx->Lock_WaitObjectsList),
flags
);
pEntry = pdx->List_WaitObjects.next;
// Find the wait object and wait for wake-up event
while (pEntry != &(pdx->List_WaitObjects))
{
// Get the wait object
pWaitObject =
list_entry(
pEntry,
PLX_WAIT_OBJECT,
ListEntry
);
// Check if the object address matches the Tag
if (pWaitObject == pUserWaitObject)
{
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
DebugPrintf((
"Waiting for Interrupt wait object (%p) to wake-up\n",
pWaitObject
));
/*********************************************************
* Convert milliseconds to jiffies. The following
* formula is used:
*
* ms * HZ
* jiffies = ---------
* 1,000
*
*
* where: HZ = System-defined clock ticks per second
* ms = Timeout in milliseconds
* jiffies = Number of HZ's per second
*
* Note: Since the timeout is stored as a "long" integer,
* the conversion to jiffies is split into two operations.
* The first is on number of seconds and the second on
* the remaining millisecond precision. This mimimizes
* overflow when the specified timeout is large and also
* keeps millisecond precision.
********************************************************/
// Perform conversion if not infinite wait
if (Timeout_ms != PLX_TIMEOUT_INFINITE)
{
// Get number of seconds
Timeout_sec = Timeout_ms / 1000;
// Store milliseconds precision
Timeout_ms = Timeout_ms - (Timeout_sec * 1000);
// Convert to jiffies
Timeout_sec = Timeout_sec * HZ;
Timeout_ms = (Timeout_ms * HZ) / 1000;
// Compute total jiffies
Timeout_ms = Timeout_sec + Timeout_ms;
}
// Timeout parameter is signed and can't be negative
if ((signed long)Timeout_ms < 0)
{
// Shift out negative bit
Timeout_ms = Timeout_ms >> 1;
}
do
{
// Wait for interrupt event
Wait_rc =
Plx_wait_event_interruptible_timeout(
pWaitObject->WaitQueue,
(pWaitObject->bPending == FALSE),
Timeout_ms
);
}
while ((Wait_rc == 0) && (Timeout_ms == PLX_TIMEOUT_INFINITE));
if (Wait_rc > 0)
{
// Condition met or interrupt occurred
DebugPrintf(("Interrupt wait object awakened\n"));
rc = ApiSuccess;
}
else if (Wait_rc == 0)
{
// Timeout reached
DebugPrintf(("Timeout waiting for interrupt\n"));
return ApiWaitTimeout;
}
else
{
// Interrupted by a signal
DebugPrintf(("Interrupt wait object interrupted by signal or error\n"));
rc = ApiWaitCanceled;
}
// Mark the object as pending to reset it
pWaitObject->bPending = TRUE;
return rc;
}
// Jump to next item in the list
pEntry = pEntry->next;
}
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
DebugPrintf((
"Interrupt wait object (%p) not found or previously canceled\n",
pUserWaitObject
));
// Object not found at this point
return ApiFailed;
}
/*******************************************************************************
*
* Function : PlxNotificationStatus
*
* Description: Returns the interrupt(s) that have caused notification events
*
******************************************************************************/
RETURN_CODE
PlxNotificationStatus(
DEVICE_EXTENSION *pdx,
VOID *pUserWaitObject,
PLX_INTERRUPT *pPlxIntr
)
{
unsigned long flags;
struct list_head *pEntry;
PLX_WAIT_OBJECT *pWaitObject;
PLX_INTERRUPT_DATA IntData;
spin_lock_irqsave(
&(pdx->Lock_WaitObjectsList),
flags
);
pEntry = pdx->List_WaitObjects.next;
// Traverse list to find the desired list object
while (pEntry != &(pdx->List_WaitObjects))
{
// Get the object
pWaitObject =
list_entry(
pEntry,
PLX_WAIT_OBJECT,
ListEntry
);
// Check if desired object
if (pWaitObject == pUserWaitObject)
{
// Copy the interrupt sources
IntData.Source_Ints = pWaitObject->Source_Ints;
IntData.Source_Doorbell = pWaitObject->Source_Doorbell;
// Reset interrupt sources
pWaitObject->Source_Ints = INTR_TYPE_NONE;
pWaitObject->Source_Doorbell = 0;
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
DebugPrintf((
"Returning status for interrupt wait object (%p)...\n",
pWaitObject
));
// Set triggered interrupts
RtlZeroMemory(
pPlxIntr,
sizeof(PLX_INTERRUPT)
);
pPlxIntr->Doorbell = IntData.Source_Doorbell;
if (IntData.Source_Ints & INTR_TYPE_MESSAGE)
pPlxIntr->Message = 1;
if (IntData.Source_Ints & INTR_TYPE_S_RSTIN)
pPlxIntr->SecResetDeassert = 1;
if (IntData.Source_Ints & INTR_TYPE_S_PME)
pPlxIntr->SecPmeDeassert = 1;
if (IntData.Source_Ints & INTR_TYPE_GPIO14)
pPlxIntr->GPIO14 = 1;
if (IntData.Source_Ints & INTR_TYPE_GPIO4)
pPlxIntr->GPIO4 = 1;
return ApiSuccess;
}
// Jump to next item in the list
pEntry = pEntry->next;
}
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
return ApiFailed;
}
/*******************************************************************************
*
* Function : PlxNotificationCancel
*
* Description: Cancels a registered notification event
*
******************************************************************************/
RETURN_CODE
PlxNotificationCancel(
DEVICE_EXTENSION *pdx,
VOID *pUserWaitObject,
VOID *pOwner
)
{
BOOLEAN bRemove;
unsigned long flags;
struct list_head *pEntry;
PLX_WAIT_OBJECT *pWaitObject;
spin_lock_irqsave(
&(pdx->Lock_WaitObjectsList),
flags
);
pEntry = pdx->List_WaitObjects.next;
// Find the object and remove it
while (pEntry != &(pdx->List_WaitObjects))
{
// Get the object
pWaitObject =
list_entry(
pEntry,
PLX_WAIT_OBJECT,
ListEntry
);
// Default to not remove
bRemove = FALSE;
// Determine if object should be removed
if (pOwner == pWaitObject->pOwner)
{
if (pUserWaitObject == NULL)
{
bRemove = TRUE;
}
else if (pWaitObject == pUserWaitObject)
{
bRemove = TRUE;
}
}
// Remove object
if (bRemove)
{
DebugPrintf((
"Removing interrupt wait object (%p)...\n",
pWaitObject
));
// Remove the object from the list
list_del(
pEntry
);
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
// Wake-up processes only if wait object is pending
if (pWaitObject->bPending == TRUE)
{
// Wake-up any process waiting on the object
wake_up_interruptible(
&(pWaitObject->WaitQueue)
);
}
// Release the list object
kfree(
pWaitObject
);
// Return if removing only a specific object
if (pUserWaitObject != NULL)
return ApiSuccess;
// Reset to beginning of list
spin_lock_irqsave(
&(pdx->Lock_WaitObjectsList),
flags
);
pEntry = pdx->List_WaitObjects.next;
}
else
{
// Jump to next item in the list
pEntry = pEntry->next;
}
}
spin_unlock_irqrestore(
&(pdx->Lock_WaitObjectsList),
flags
);
return ApiFailed;
}