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_8000.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 RegValue;
// Reset value
*pChipType = 0;
// Check PCI E0h for hard-coded ID
PLX_PCI_REG_READ(
pdx,
0xE0,
&RegValue
);
if ((RegValue & 0xFFFF) == PLX_VENDOR_ID)
{
*pChipType = (U16)(RegValue >> 16);
// PLX revision should be in PCI E4h
PLX_PCI_REG_READ(
pdx,
0xE4,
&RegValue
);
*pRevision = (U8)(RegValue & 0xFF);
goto _Exit_PlxChipTypeGet;
}
// Get revision if not yet assigned
if (pKey->Revision == 0)
{
// Read revision ID from device
PLX_PCI_REG_READ(
pdx,
0x08, // Class code/revision ID
&RegValue
);
pKey->Revision = (U8)RegValue;
}
// Set revision
*pRevision = pKey->Revision;
// If hard-coded ID doesn't exist, use Device/Vendor ID
RegValue = ((U32)pKey->DeviceId << 16) | pKey->VendorId;
switch (RegValue)
{
case 0x850810B5: // 8508
case 0x851210B5: // 8512
case 0x851610B5: // 8516
case 0x851710B5: // 8517
case 0x851810B5: // 8518
case 0x852410B5: // 8524
case 0x853210B5: // 8532
*pChipType = pKey->DeviceId;
break;
default:
DebugPrintf(("ERROR - Unable to determine chip type\n"));
return ApiInvalidDeviceInfo;
}
_Exit_PlxChipTypeGet:
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
)
{
U16 Offset_PcieCap;
U32 RegValue;
// Get the offset of the PCI Express capability
Offset_PcieCap =
PlxGetExtendedCapabilityOffset(
pdx,
0x10 // CAP_ID_PCI_EXPRESS
);
if (Offset_PcieCap == 0)
{
DebugPrintf(("Device does not support PCI Express Capability\n"));
// Set default value for properties
RtlZeroMemory(pPortProp, sizeof(PLX_PORT_PROP));
pPortProp->PortType = PLX_PORT_NON_TRANS;
return ApiSuccess;
}
// Get PCIe Capability
PLX_PCI_REG_READ(
pdx,
Offset_PcieCap,
&RegValue
);
// Get port type
pPortProp->PortType = (U8)((RegValue >> 20) & 0xF);
// Get PCIe Device Control
PLX_PCI_REG_READ(
pdx,
Offset_PcieCap + 0x08,
&RegValue
);
// Get max payload size field
RegValue = (RegValue >> 5) & 0x7;
// Set max payload size (=128 * (2^MaxPaySizeloadField))
switch (RegValue)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
pPortProp->MaxPayloadSize = 128 * (2 ^ RegValue);
break;
default:
pPortProp->MaxPayloadSize = 0;
break;
}
// Get PCIe Link Capabilities
PLX_PCI_REG_READ(
pdx,
Offset_PcieCap + 0x0C,
&RegValue
);
// Get port number
pPortProp->PortNumber = (U8)((RegValue >> 24) & 0xFF);
// Get max link width
pPortProp->MaxLinkWidth = (U8)((RegValue >> 4) & 0x3F);
// Get max link speed
pPortProp->MaxLinkSpeed = (U8)((RegValue >> 0) & 0xF);
// Get PCIe Link Status/Control
PLX_PCI_REG_READ(
pdx,
Offset_PcieCap + 0x10,
&RegValue
);
// Get link width
pPortProp->LinkWidth = (U8)((RegValue >> 20) & 0x3F);
// Get link speed
pPortProp->LinkSpeed = (U8)((RegValue >> 16) & 0xF);
DebugPrintf((
"PortType=%d PortNumber=%d MaxPayload=%d LinkWidth=x%d (max=x%d)\n",
pPortProp->PortType, pPortProp->PortNumber,
pPortProp->MaxPayloadSize, pPortProp->LinkWidth,
pPortProp->MaxLinkWidth
));
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
)
{
U32 value;
// Verify register offset
if (offset >= 0x12000)
{
DebugPrintf(("ERROR - Invalid register offset (0x%x)\n", offset));
if (pReturnCode != NULL)
*pReturnCode = ApiInvalidOffset;
return 0;
}
if (pReturnCode != NULL)
*pReturnCode = ApiSuccess;
// Adjust the offset for correct port
if (bAdjustForPort)
{
if (pdx->bFromLinkSide == TRUE)
{
// Link side is 11000h from base of upstream port
offset += 0x11000;
}
else
{
// Virtual side is 10000h from base of upstream port
offset += 0x10000;
}
}
// Read value
value =
PLX_8000_REG_READ(
pdx,
offset
);
return value;
}
/*******************************************************************************
*
* 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
)
{
// Verify register offset
if (offset >= 0x12000)
{
DebugPrintf(("ERROR - Invalid register offset (0x%x)\n", offset));
return ApiInvalidOffset;
}
// Adjust the offset for correct port
if (bAdjustForPort)
{
if (pdx->bFromLinkSide == TRUE)
{
// Link side is 11000h from base of upstream port
offset += 0x11000;
}
else
{
// Virtual side is 10000h from base of upstream port
offset += 0x10000;
}
}
// Write the value
PLX_8000_REG_WRITE(
pdx,
offset,
value
);
return ApiSuccess;
}
/*******************************************************************************
*
* 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
)
{
return Plx8000_EepromPresent(
pdx,
pStatus
);
}
/*******************************************************************************
*
* Function : PlxEepromProbe
*
* Description: Probes for the presence of an EEPROM
*
******************************************************************************/
RETURN_CODE
PlxEepromProbe(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
BOOLEAN *pFlag
)
{
U16 OffsetProbe;
U32 ValueRead;
U32 ValueWrite;
U32 ValueOriginal;
RETURN_CODE rc;
// Default to no EEPROM present
*pFlag = FALSE;
// Determine EEPROM offset to use for probe (e.g. after CRC)
switch (pdx->PlxChipType)
{
case 0x8508:
case 0x8512:
case 0x8517:
case 0x8518:
OffsetProbe = (0x78F * sizeof(U32)) + sizeof(U32);
break;
case 0x8516:
case 0x8524:
case 0x8532:
OffsetProbe = (0xBE4 * sizeof(U32)) + sizeof(U32);
break;
case 0:
default:
DebugPrintf((
"ERROR - Not a supported PLX device (%04X)\n",
pdx->PlxChipType
));
return ApiUnsupportedFunction;
}
DebugPrintf(("Probing EEPROM at offset %02xh\n", OffsetProbe));
// Get the current value
rc =
PlxEepromReadByOffset(
pdx,
pKey,
OffsetProbe,
&ValueOriginal
);
if (rc != ApiSuccess)
return rc;
// Prepare inverse value to write
ValueWrite = ~(ValueOriginal);
// Write inverse of original value
rc =
PlxEepromWriteByOffset(
pdx,
pKey,
OffsetProbe,
ValueWrite
);
if (rc != ApiSuccess)
return rc;
// Read updated value
rc =
PlxEepromReadByOffset(
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(
pdx,
pKey,
OffsetProbe,
ValueOriginal
);
}
else
{
DebugPrintf(("Probe did not detect an EEPROM\n"));
}
return ApiSuccess;
}
/*******************************************************************************
*
* Function : PlxEepromSetAddressWidth
*
* Description: Sets a new EEPROM address width
*
******************************************************************************/
RETURN_CODE
PlxEepromSetAddressWidth(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U8 width
)
{
RETURN_CODE rc;
// Verify the width
switch (width)
{
case 1:
case 2:
case 3:
break;
default:
DebugPrintf((
"ERROR - Invalid address width (%d)\n",
width
));
return ApiInvalidData;
}
rc =
Plx8000_EepromSetAddressWidth(
pdx,
width
);
if (rc == ApiSuccess)
{
DebugPrintf((
"Set EEPROM address width to %d bytes\n",
width
));
}
return rc;
}
/*******************************************************************************
*
* Function : PlxEepromCrcGet
*
* Description: Returns the EEPROM CRC and its status
*
******************************************************************************/
RETURN_CODE
PlxEepromCrcGet(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 *pCrc,
U8 *pCrcStatus
)
{
switch (pdx->PlxChipType)
{
case 0x8508:
case 0x8512:
case 0x8516:
case 0x8517:
case 0x8518:
case 0x8524:
case 0x8532:
return Plx8000_EepromCrcGet(
pdx,
pCrc,
pCrcStatus
);
}
// Clear return values
*pCrc = 0;
*pCrcStatus = PLX_CRC_VALID;
// Devices don't support CRC
return ApiUnsupportedFunction;
}
/*******************************************************************************
*
* Function : PlxEepromCrcUpdate
*
* Description: Updates the EEPROM CRC
*
******************************************************************************/
RETURN_CODE
PlxEepromCrcUpdate(
DEVICE_EXTENSION *pdx,
PLX_DEVICE_KEY *pKey,
U32 *pCrc,
BOOLEAN bUpdateEeprom
)
{
switch (pdx->PlxChipType)
{
case 0x8508:
case 0x8512:
case 0x8516:
case 0x8517:
case 0x8518:
case 0x8524:
case 0x8532:
return Plx8000_EepromCrcUpdate(
pdx,
pCrc,
bUpdateEeprom
);
}
// Clear return value
*pCrc = 0;
// Devices don't 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;
}
return Plx8000_EepromReadByOffset(
pdx,
offset,
pValue
);
}
/*******************************************************************************
*
* 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;
}
return Plx8000_EepromWriteByOffset(
pdx,
offset,
value
);
}
/*******************************************************************************
*
* 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
)
{
U32 Value_32;
RETURN_CODE rc;
// Make sure offset is aligned on 16-bit boundary
if (offset & (1 << 0))
{
*pValue = 0;
return ApiInvalidOffset;
}
/******************************************
* For devices that do not support 16-bit
* EEPROM accesses, use 32-bit access
*****************************************/
// Get current 32-bit value
rc =
PlxEepromReadByOffset(
pdx,
pKey,
(offset & ~0x3),
&Value_32
);
if (rc != ApiSuccess)
{
*pValue = 0;
return rc;
}
// Return desired 16-bit portion
if ((offset & 0x3) == 2)
{
*pValue = (U16)(Value_32 >> 16);
}
else
{
*pValue = (U16)(Value_32);
}
return ApiSuccess;
}
/*******************************************************************************
*
* 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
)
{
U32 Value_32;
RETURN_CODE rc;
// Make sure offset is aligned on 16-bit boundary
if (offset & (1 << 0))
{
return ApiInvalidOffset;
}
/******************************************
* For devices that do not support 16-bit
* EEPROM accesses, use 32-bit access
*****************************************/
// Get current 32-bit value
rc =
PlxEepromReadByOffset(
pdx,
pKey,
(offset & ~0x3),
&Value_32
);
if (rc != ApiSuccess)
return rc;
// Insert new 16-bit value in correct location
if ((offset & 0x3) == 2)
{
Value_32 = ((U32)value << 16) | (Value_32 & 0xFFFF);
}
else
{
Value_32 = ((U32)value) | (Value_32 & 0xFFFF0000);
}
// Update EEPROM
return PlxEepromWriteByOffset(
pdx,
pKey,
(offset & ~0x3),
Value_32
);
}
/*******************************************************************************
*
* 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;
// 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;
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;
}