www.pudn.com > 9054Src.rar > PciSupport.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:
*
* PciSupport.c
*
* Description:
*
* This file contains the PCI support functions
*
* Revision History:
*
* 02-01-06 : PCI SDK v4.40
*
******************************************************************************/
#include "PciSupport.h"
#include "PlugPlay.h"
#include "PlxChip.h"
/******************************************************************************
*
* Function : PlxPciRegisterRead
*
* Description: Reads a PCI register at the specified offset
*
******************************************************************************/
U32
PlxPciRegisterRead(
DEVICE_EXTENSION *pdx,
U8 bus,
U8 slot,
U16 offset,
RETURN_CODE *pReturnCode
)
{
U32 RegisterValue;
NTSTATUS status;
PDEVICE_OBJECT fdo;
if (pdx->pDeviceObject == NULL)
{
if (pReturnCode != NULL)
*pReturnCode = ApiNullParam;
return (U32)-1;
}
if (pdx->pDeviceObject->DriverObject == NULL)
{
if (pReturnCode != NULL)
*pReturnCode = ApiNullParam;
return (U32)-1;
}
fdo = pdx->pDeviceObject->DriverObject->DeviceObject;
// Now parse through all the functional devices objects
while (fdo != NULL)
{
pdx = fdo->DeviceExtension;
if (pdx == NULL)
{
DebugPrintf(("ERROR - ConfigRegisterRead() NULL driver object\n"));
if (pReturnCode != NULL)
*pReturnCode = ApiConfigAccessFailed;
return (U32)-1;
}
// Compare the bus and slot numbers
if ((bus == pdx->Device.BusNumber) &&
(slot == pdx->Device.SlotNumber))
{
// Read the register
status =
PciRegisterBufferRead(
fdo,
offset,
&RegisterValue,
sizeof(U32)
);
if ( !NT_SUCCESS(status) )
{
DebugPrintf(("ERROR - Unable to read PCI Config Register\n"));
if (pReturnCode != NULL)
*pReturnCode = ApiConfigAccessFailed;
return (U32)-1;
}
if (pReturnCode != NULL)
*pReturnCode = ApiSuccess;
return RegisterValue;
}
// Increment to next device
fdo = fdo->NextDevice;
}
// If we reach here, then the device was not located
if (pReturnCode != NULL)
*pReturnCode = ApiConfigAccessFailed;
return (U32)-1;
}
/******************************************************************************
*
* Function : PlxPciRegisterWrite
*
* Description: Writes a value to a PCI register at the specified offset
*
******************************************************************************/
RETURN_CODE
PlxPciRegisterWrite(
DEVICE_EXTENSION *pdx,
U8 bus,
U8 slot,
U16 offset,
U32 value
)
{
NTSTATUS status;
PDEVICE_OBJECT fdo;
if (pdx->pDeviceObject == NULL)
return ApiNullParam;
if (pdx->pDeviceObject->DriverObject == NULL)
return ApiNullParam;
fdo = pdx->pDeviceObject->DriverObject->DeviceObject;
// Now parse through all the functional devices objects
while (fdo != NULL)
{
pdx = fdo->DeviceExtension;
if (pdx == NULL)
{
DebugPrintf(("ERROR - ConfigRegisterRead() NULL driver object\n"));
return ApiConfigAccessFailed;
}
// Compare the bus and slot numbers
if ((bus == pdx->Device.BusNumber) &&
(slot == pdx->Device.SlotNumber))
{
// Read the register
status =
PciRegisterBufferWrite(
fdo,
offset,
value
);
if ( !NT_SUCCESS(status) )
{
DebugPrintf(("ERROR - Unable to write PCI Config Register\n"));
return ApiConfigAccessFailed;
}
return ApiSuccess;
}
// Increment to next device
fdo = fdo->NextDevice;
}
// If we reach here, then the device was not located
return ApiConfigAccessFailed;
}
/******************************************************************************
*
* Function : PciRegisterBufferRead
*
* Description: Read a PCI register of a device using the PnP Bus Manager
*
******************************************************************************/
NTSTATUS
PciRegisterBufferRead(
PDEVICE_OBJECT fdo,
U16 offset,
VOID *buffer,
U16 size
)
{
PIRP pIrp;
KEVENT event;
NTSTATUS status;
PIO_STACK_LOCATION stack;
pIrp =
IoAllocateIrp(
fdo->StackSize,
FALSE
);
if (pIrp == NULL)
{
DebugPrintf(("ERROR - ConfigRegisterBufferRead() unable to allocate IRP\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// Initialize kernel event
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
stack =
IoGetNextIrpStackLocation(
pIrp
);
// Fill the IRP
pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
stack->MajorFunction = IRP_MJ_PNP;
stack->MinorFunction = IRP_MN_READ_CONFIG;
stack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG;
stack->Parameters.ReadWriteConfig.Buffer = buffer;
stack->Parameters.ReadWriteConfig.Offset = offset;
stack->Parameters.ReadWriteConfig.Length = size;
IoSetCompletionRoutine(
pIrp,
(PIO_COMPLETION_ROUTINE)OnRequestComplete,
(PVOID)&event,
TRUE,
TRUE,
TRUE
);
// Send the packet
status =
IoCallDriver(
((DEVICE_EXTENSION *) fdo->DeviceExtension)->pLowerDeviceObject,
pIrp
);
if (status == STATUS_PENDING)
{
// Wait for completion
KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
status = pIrp->IoStatus.Status;
}
// Release the IRP
IoFreeIrp(
pIrp
);
return status;
}
/******************************************************************************
*
* Function : PciRegisterBufferWrite
*
* Description: Writes to a PCI register of a device using the PnP Bus Manager
*
******************************************************************************/
NTSTATUS
PciRegisterBufferWrite(
PDEVICE_OBJECT fdo,
U16 offset,
U32 value
)
{
PIRP pIrp;
KEVENT event;
NTSTATUS status;
PIO_STACK_LOCATION stack;
pIrp =
IoAllocateIrp(
fdo->StackSize,
FALSE
);
if (pIrp == NULL)
{
DebugPrintf(("ERROR - ConfigRegisterBufferWrite() unable to allocate IRP\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// Initialize kernel event
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
stack =
IoGetNextIrpStackLocation(
pIrp
);
// Fill the IRP
pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
stack->MajorFunction = IRP_MJ_PNP;
stack->MinorFunction = IRP_MN_WRITE_CONFIG;
stack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG;
stack->Parameters.ReadWriteConfig.Buffer = &value;
stack->Parameters.ReadWriteConfig.Offset = offset;
stack->Parameters.ReadWriteConfig.Length = sizeof(U32);
IoSetCompletionRoutine(
pIrp,
(PIO_COMPLETION_ROUTINE)OnRequestComplete,
(PVOID)&event,
TRUE,
TRUE,
TRUE
);
// Send the packet
status =
IoCallDriver(
((DEVICE_EXTENSION *)fdo->DeviceExtension)->pLowerDeviceObject,
pIrp
);
if (status == STATUS_PENDING)
{
// Wait for completion
KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
status = pIrp->IoStatus.Status;
}
IoFreeIrp(
pIrp
);
return STATUS_SUCCESS;
}
/******************************************************************************
*
* Function : PlxPciRegisterRead_Unsupported
*
* Description: Reads a PCI register by bypassing the OS services
*
******************************************************************************/
U32
PlxPciRegisterRead_Unsupported(
DEVICE_EXTENSION *pdx,
U8 bus,
U8 slot,
U16 offset,
RETURN_CODE *pReturnCode
)
{
U32 function;
U32 RegSave;
U32 RegValue;
KIRQL IrqlSave;
/***************************************************************
* Access of a PCI register involves using I/O addresses 0xcf8
* and 0xcfc. These addresses must be used together and no other
* process must interrupt.
*
* Note: On Multi-Processor systems, the I/O ports may still be
* accessed by another CPU. Raising the IRQL does not
* prevent other CPUs from accessing the ports since
* they are not a resource owned by this driver.
**************************************************************/
// The function number is specified in the upper 3-bits of the slot
function = (slot >> 5) & 0x7;
// Make sure upper 3-bits are cleared
slot &= 0x1F;
// Raise the IRQL to prevent context switches during access
KeRaiseIrql(
DISPATCH_LEVEL,
&IrqlSave
);
// Save the content of the command register
RegSave =
IO_PORT_READ_32(
0xcf8
);
// Configure the command register to access the desired location
IO_PORT_WRITE_32(
0xcf8,
(1 << 31) | (bus << 16) | (slot << 11) | (function << 8) | offset
);
// Read the register
RegValue =
IO_PORT_READ_32(
0xcfc
);
// Restore the command register
IO_PORT_WRITE_32(
0xcf8,
RegSave
);
// Restore IRQL
KeLowerIrql(
IrqlSave
);
if (pReturnCode != NULL)
*pReturnCode = ApiSuccess;
return RegValue;
}
/******************************************************************************
*
* Function : PlxPciRegisterWrite_Unsupported
*
* Description: Writes to a PCI register by bypassing the OS services
*
******************************************************************************/
RETURN_CODE
PlxPciRegisterWrite_Unsupported(
DEVICE_EXTENSION *pdx,
U8 bus,
U8 slot,
U16 offset,
U32 value
)
{
U8 function;
U32 RegSave;
KIRQL IrqlSave;
/***************************************************************
* Access of a PCI register involves using I/O addresses 0xcf8
* and 0xcfc. These addresses must be used together and no other
* process must interrupt.
*
* Note: On Multi-Processor systems, the I/O ports may still be
* accessed by another CPU. Raising the IRQL does not
* prevent other CPUs from accessing the ports since
* they are not a resource owned by this driver.
**************************************************************/
// The function number is specified in the upper 3-bits of the slot
function = (slot >> 5) & 0x7;
// Make sure upper 3-bits are cleared
slot &= 0x1F;
// Raise the IRQL to prevent context switches during access
KeRaiseIrql(
DISPATCH_LEVEL,
&IrqlSave
);
// Save the content of the command register
RegSave =
IO_PORT_READ_32(
0xcf8
);
// Configure the command register to access the desired location
IO_PORT_WRITE_32(
0xcf8,
(1 << 31) | (bus << 16) | (slot << 11) | (function << 8) | offset
);
// Write the register
IO_PORT_WRITE_32(
0xcfc,
value
);
// Restore the command register
IO_PORT_WRITE_32(
0xcf8,
RegSave
);
// Restore IRQL
KeLowerIrql(
IrqlSave
);
return ApiSuccess;
}
/******************************************************************************
*
* Function : GetBusSlotNumber
*
* Description: Get the bus and slot number of a supported device.
* This function does not work for the slot number.
*
******************************************************************************/
NTSTATUS
GetBusSlotNumber(
PDEVICE_OBJECT pdo,
DEVICE_EXTENSION *pdx,
U32 CompareValue
)
{
U8 slot;
U32 ResultLength;
U32 PropertyBuffer;
U32 RegValue;
U32 RegisterSave;
NTSTATUS status;
ResultLength = 0;
// Get the bus number
status =
IoGetDeviceProperty(
pdo,
DevicePropertyBusNumber,
sizeof(U32),
&PropertyBuffer,
&ResultLength
);
if ( !NT_SUCCESS(status) )
{
DebugPrintf((
"ERROR - IoGetDeviceProperty() unable to get bus number, code = %Xh\n",
status
));
return status;
}
// Verify that the function did write back a U32
if (ResultLength != sizeof(U32))
{
DebugPrintf((
"ERROR - IoGetDeviceProperty() invalid ResultLength (%d)\n",
ResultLength
));
return STATUS_UNSUCCESSFUL;
}
// Store the Bus number
pdx->Device.BusNumber = (U8)PropertyBuffer;
/**************************************************************************
* Now attempt to get the slot number
*
* Note: The IoGetDeviceProperty() function is not implemented correctly in
* Windows 98. The code has been left here in case this is fixed in a
* future release. If the IoGetDeviceProperty() function is unsuccessful,
* a workaround is provided to retrieve the correct slot number.
**************************************************************************/
ResultLength = 0;
status =
IoGetDeviceProperty(
pdo,
DevicePropertyAddress,
sizeof(U32),
&PropertyBuffer,
&ResultLength
);
if ( NT_SUCCESS(status) )
{
if (ResultLength != sizeof(U32))
{
DebugPrintf((
"ERROR - IoGetDeviceProperty() invalid ResultLength (%d)\n",
ResultLength
));
return STATUS_UNSUCCESSFUL;
}
pdx->Device.SlotNumber = (U8)(PropertyBuffer >> 16);
DebugPrintf((
"Device information - PCI bus %d, slot %d, function %d\n",
pdx->Device.BusNumber,
(U8)(PropertyBuffer >> 16),
(U8)PropertyBuffer
));
return STATUS_SUCCESS;
}
/****************************************************************
* We were unable to get the slot number, so another method is
* required. Our workaround is to scan the PCI bus and find the
* device whose BAR0 matches the current device.
****************************************************************/
DebugPrintf(("WARNING - IoGetDeviceProperty() unable to get slot number, code = "));
switch (status)
{
case STATUS_BUFFER_TOO_SMALL:
DebugPrintf_NoInfo(("STATUS_BUFFER_TOO_SMALL (req=%d bytes)\n", ResultLength));
break;
case STATUS_NOT_IMPLEMENTED:
DebugPrintf_NoInfo(("STATUS_NOT_IMPLEMENTED\n"));
break;
case STATUS_NOT_FOUND:
DebugPrintf_NoInfo(("STATUS_NOT_FOUND\n"));
break;
default:
DebugPrintf_NoInfo(("%08xh\n", status));
break;
}
DebugPrintf(("NOTE: Implementing workaround to get Slot number\n"));
// Scan the PCI bus to find our device
for (slot = 0; slot < MAX_PCI_DEV; slot++)
{
// Read the BAR register
RegValue =
PlxPciRegisterRead_Unsupported(
pdx,
pdx->Device.BusNumber,
slot,
CFG_BAR0,
NULL
);
// Compare with our device
if (RegValue == CompareValue)
{
pdx->Device.SlotNumber = slot;
DebugPrintf((
"Workaround successful, device information - PCI bus %d, slot %d, function 0\n",
pdx->Device.BusNumber,
slot
));
return STATUS_SUCCESS;
}
}
DebugPrintf(("ERROR - Unable to implement workaround to get Slot number\n"));
pdx->Device.SlotNumber = -1;
return STATUS_UNSUCCESSFUL;
}
/******************************************************************************
*
* Function : PlxLockDevice
*
* Description: Lock a device for operation, return FALSE if device is being
* removed.
*
******************************************************************************/
BOOLEAN
PlxLockDevice(
DEVICE_EXTENSION *pdx
)
{
S32 usage;
// Increment use count on our device object
usage =
InterlockedIncrement(
&pdx->usage
);
DebugPrintf_NoInfo(("\n"));
/*
If device is about to be removed, restore the use count and return FALSE.
If a race exists with HandleRemoveDevice (maybe running on another CPU),
the sequence we've followed is guaranteed to avoid a mistaken deletion of
the device object. If we test "removing" after HandleRemoveDevice sets
it, we'll restore the use count and return FALSE. In the meantime, if
HandleRemoveDevice decremented count to 0 before we did our increment,
its thread will have set the remove event. Otherwise, we'll decrement to
0 and set the event. Either way, HandleRemoveDevice will wake up to
finish removing the device, and we'll return FALSE to our caller.
If, on the other hand, we test "removing" before HandleRemoveDevice sets
it, we'll have already incremented the use count past 1 and will return
TRUE. Our caller will eventually call UnlockDevice, which will decrement
the use count and might set the event HandleRemoveDevice is waiting on at
that point.
*/
if (pdx->removing)
{
// Removing device
if (InterlockedDecrement(&pdx->usage) == 0)
{
KeSetEvent(
&pdx->evRemove,
0,
FALSE
);
}
return FALSE;
}
return TRUE;
}
/******************************************************************************
*
* Function : PlxUnlockDevice
*
* Description: Unlock a device
*
******************************************************************************/
VOID
PlxUnlockDevice(
DEVICE_EXTENSION *pdx
)
{
S32 usage;
usage =
InterlockedDecrement(
&pdx->usage
);
if (usage == 0)
{
// Removing device
KeSetEvent(
&pdx->evRemove,
0,
FALSE
);
}
}