mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
17 KiB
713 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
port.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Ken Reneris (kenr) March-13-1885
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pciport.h"
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,PcipAssignSlotResources)
|
|
#pragma alloc_text(PAGE,PcipQueryBusSlots)
|
|
#pragma alloc_text(PAGE,PcipHibernateBus)
|
|
#pragma alloc_text(PAGE,PcipResumeBus)
|
|
#pragma alloc_text(PAGE,PcipSuspendNotification)
|
|
#pragma alloc_text(PAGE,PcipReferenceDeviceHandler)
|
|
#endif
|
|
|
|
|
|
ULONG
|
|
PcipGetBusData (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function returns the Pci bus data for a device.
|
|
|
|
Arguments:
|
|
|
|
BusNumber - Indicates which bus.
|
|
|
|
VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word)
|
|
|
|
Buffer - Supplies the space to store the data.
|
|
|
|
Length - Supplies a count in bytes of the maximum amount to return.
|
|
|
|
Return Value:
|
|
|
|
Returns the amount of data stored into the buffer.
|
|
|
|
If this PCI slot has never been set, then the configuration information
|
|
returned is zeroed.
|
|
|
|
--*/
|
|
{
|
|
PPCI_COMMON_CONFIG PciData;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
PPCIBUSDATA BusData;
|
|
ULONG Len;
|
|
ULONG i, bit;
|
|
|
|
BusData = (PPCIBUSDATA) BusHandler->BusData;
|
|
|
|
if (Length > sizeof (PCI_COMMON_CONFIG)) {
|
|
Length = sizeof (PCI_COMMON_CONFIG);
|
|
}
|
|
|
|
Len = 0;
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
//
|
|
// The user did not request any data from the common
|
|
// header. Verify the PCI device exists, then continue
|
|
// in the device specific area.
|
|
//
|
|
|
|
BusData->ReadConfig (BusHandler, SlotNumber, PciData, 0, sizeof (ULONG));
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID) {
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Caller requested at least some data within the
|
|
// common header. Read the whole header, effect the
|
|
// fields we need to and then copy the user's requested
|
|
// bytes from the header
|
|
//
|
|
|
|
|
|
//
|
|
// Read this PCI devices slot data
|
|
//
|
|
|
|
Len = PCI_COMMON_HDR_LENGTH;
|
|
BusData->ReadConfig (BusHandler, SlotNumber, PciData, 0, Len);
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID ||
|
|
PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) {
|
|
PciData->VendorID = PCI_INVALID_VENDORID;
|
|
Len = 2; // only return invalid id
|
|
|
|
} else {
|
|
|
|
BusData->Pin2Line (BusHandler, RootHandler, SlotNumber, PciData);
|
|
}
|
|
|
|
//
|
|
// Copy whatever data overlaps into the callers buffer
|
|
//
|
|
|
|
if (Len < Offset) {
|
|
// no data at caller's buffer
|
|
return 0;
|
|
}
|
|
|
|
Len -= Offset;
|
|
if (Len > Length) {
|
|
Len = Length;
|
|
}
|
|
|
|
RtlMoveMemory(Buffer, iBuffer + Offset, Len);
|
|
|
|
Offset += Len;
|
|
Buffer += Len;
|
|
Length -= Len;
|
|
}
|
|
|
|
if (Length) {
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
//
|
|
// The remaining Buffer comes from the Device Specific
|
|
// area - put on the kitten gloves and read from it.
|
|
//
|
|
// Specific read/writes to the PCI device specific area
|
|
// are guarenteed:
|
|
//
|
|
// Not to read/write any byte outside the area specified
|
|
// by the caller. (this may cause WORD or BYTE references
|
|
// to the area in order to read the non-dword aligned
|
|
// ends of the request)
|
|
//
|
|
// To use a WORD access if the requested length is exactly
|
|
// a WORD long.
|
|
//
|
|
// To use a BYTE access if the requested length is exactly
|
|
// a BYTE long.
|
|
//
|
|
|
|
BusData->ReadConfig (BusHandler, SlotNumber, Buffer, Offset, Length);
|
|
Len += Length;
|
|
}
|
|
}
|
|
|
|
return Len;
|
|
}
|
|
|
|
|
|
ULONG
|
|
PcipGetDeviceData (
|
|
IN struct _BUS_HANDLER *BusHandler,
|
|
IN struct _BUS_HANDLER *RootHandler,
|
|
IN PDEVICE_HANDLER_OBJECT DeviceHandler,
|
|
IN ULONG DataType,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
ULONG Status;
|
|
PDEVICE_DATA DeviceData;
|
|
PCI_SLOT_NUMBER SlotNumber;
|
|
|
|
Status = 0;
|
|
DeviceData = DeviceHandler2DeviceData (DeviceHandler);
|
|
|
|
//
|
|
// Verify caller has a valid DeviceHandler object
|
|
//
|
|
|
|
if (!DeviceData->Valid) {
|
|
|
|
//
|
|
// Obsolete object, return no data
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Get the device's data.
|
|
//
|
|
|
|
if (DataType == 0) {
|
|
|
|
//
|
|
// Type 0 is the same as GetBusData for the slot
|
|
//
|
|
|
|
SlotNumber.u.AsULONG = DeviceHandler->SlotNumber;
|
|
Status = PcipGetBusData (
|
|
BusHandler,
|
|
RootHandler,
|
|
SlotNumber,
|
|
Buffer,
|
|
Offset,
|
|
Length
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
ULONG
|
|
PcipSetBusData (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
PPCI_COMMON_CONFIG PciData, PciData2;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
UCHAR iBuffer2[PCI_COMMON_HDR_LENGTH];
|
|
PPCIBUSDATA BusData;
|
|
ULONG Len, cnt;
|
|
|
|
BusData = (PPCIBUSDATA) BusHandler->BusData;
|
|
|
|
if (Length > sizeof (PCI_COMMON_CONFIG)) {
|
|
Length = sizeof (PCI_COMMON_CONFIG);
|
|
}
|
|
|
|
Len = 0;
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
PciData2 = (PPCI_COMMON_CONFIG) iBuffer2;
|
|
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
//
|
|
// The user did not request any data from the common
|
|
// header. Verify the PCI device exists, then continue in
|
|
// the device specific area.
|
|
//
|
|
|
|
BusData->ReadConfig (BusHandler, SlotNumber, PciData, 0, sizeof(ULONG));
|
|
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID) {
|
|
return 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Caller requested to set at least some data within the
|
|
// common header.
|
|
//
|
|
|
|
Len = PCI_COMMON_HDR_LENGTH;
|
|
BusData->ReadConfig (BusHandler, SlotNumber, PciData, 0, Len);
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID ||
|
|
PCI_CONFIG_TYPE (PciData) != PCI_DEVICE_TYPE) {
|
|
|
|
// no device, or header type unkown
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Copy COMMON_HDR values to buffer2, then overlay callers changes.
|
|
//
|
|
|
|
RtlMoveMemory (iBuffer2, iBuffer, Len);
|
|
BusData->Pin2Line (BusHandler, RootHandler, SlotNumber, PciData2);
|
|
|
|
Len -= Offset;
|
|
if (Len > Length) {
|
|
Len = Length;
|
|
}
|
|
|
|
RtlMoveMemory (iBuffer2+Offset, Buffer, Len);
|
|
|
|
// in case interrupt line or pin was editted
|
|
BusData->Line2Pin (BusHandler, RootHandler, SlotNumber, PciData2, PciData);
|
|
|
|
#if DBG
|
|
//
|
|
// Verify R/O fields haven't changed
|
|
//
|
|
if (PciData2->VendorID != PciData->VendorID ||
|
|
PciData2->DeviceID != PciData->DeviceID ||
|
|
PciData2->RevisionID != PciData->RevisionID ||
|
|
PciData2->ProgIf != PciData->ProgIf ||
|
|
PciData2->SubClass != PciData->SubClass ||
|
|
PciData2->BaseClass != PciData->BaseClass ||
|
|
PciData2->HeaderType != PciData->HeaderType ||
|
|
PciData2->BaseClass != PciData->BaseClass ||
|
|
PciData2->u.type0.MinimumGrant != PciData->u.type0.MinimumGrant ||
|
|
PciData2->u.type0.MaximumLatency != PciData->u.type0.MaximumLatency) {
|
|
DbgPrint ("PCI SetBusData: Read-Only configuration value changed\n");
|
|
DbgBreakPoint ();
|
|
}
|
|
#endif
|
|
//
|
|
// Set new PCI configuration
|
|
//
|
|
|
|
BusData->WriteConfig (BusHandler, SlotNumber, iBuffer2+Offset, Offset, Len);
|
|
|
|
Offset += Len;
|
|
Buffer += Len;
|
|
Length -= Len;
|
|
}
|
|
|
|
if (Length) {
|
|
if (Offset >= PCI_COMMON_HDR_LENGTH) {
|
|
//
|
|
// The remaining Buffer comes from the Device Specific
|
|
// area - put on the kitten gloves and write it
|
|
//
|
|
// Specific read/writes to the PCI device specific area
|
|
// are guarenteed:
|
|
//
|
|
// Not to read/write any byte outside the area specified
|
|
// by the caller. (this may cause WORD or BYTE references
|
|
// to the area in order to read the non-dword aligned
|
|
// ends of the request)
|
|
//
|
|
// To use a WORD access if the requested length is exactly
|
|
// a WORD long.
|
|
//
|
|
// To use a BYTE access if the requested length is exactly
|
|
// a BYTE long.
|
|
//
|
|
|
|
BusData->WriteConfig (BusHandler, SlotNumber, Buffer, Offset, Length);
|
|
Len += Length;
|
|
}
|
|
}
|
|
|
|
return Len;
|
|
}
|
|
|
|
ULONG
|
|
PcipSetDeviceData (
|
|
IN struct _BUS_HANDLER *BusHandler,
|
|
IN struct _BUS_HANDLER *RootHandler,
|
|
IN PDEVICE_HANDLER_OBJECT DeviceHandler,
|
|
IN ULONG DataType,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
ULONG Status;
|
|
PDEVICE_DATA DeviceData;
|
|
PCI_SLOT_NUMBER SlotNumber;
|
|
|
|
Status = 0;
|
|
DeviceData = DeviceHandler2DeviceData (DeviceHandler);
|
|
|
|
//
|
|
// Verify caller has a valid DeviceHandler object
|
|
//
|
|
|
|
if (!DeviceData->Valid) {
|
|
|
|
//
|
|
// Obsolete object, return no data
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Get the device's data.
|
|
//
|
|
|
|
if (DataType == 0) {
|
|
|
|
//
|
|
// Type 0 is the same as SetBusData for the slot
|
|
//
|
|
|
|
SlotNumber.u.AsULONG = DeviceHandler->SlotNumber;
|
|
Status = PcipGetBusData (
|
|
BusHandler,
|
|
RootHandler,
|
|
SlotNumber,
|
|
Buffer,
|
|
Offset,
|
|
Length
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcipAssignSlotResources (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PUNICODE_STRING DriverClassName OPTIONAL,
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN ULONG SlotNumber,
|
|
IN OUT PCM_RESOURCE_LIST *AllocatedResources
|
|
)
|
|
{
|
|
CTL_ASSIGN_RESOURCES AssignResources;
|
|
KEVENT CompletionEvent;
|
|
ULONG BufferSize;
|
|
PDEVICE_HANDLER_OBJECT DeviceHandler;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Foreward this request through a DeviceControl such that it
|
|
// gets the proper synchronzation on the device
|
|
//
|
|
|
|
DeviceHandler = BusHandler->ReferenceDeviceHandler (
|
|
BusHandler,
|
|
BusHandler,
|
|
SlotNumber
|
|
);
|
|
|
|
if (!DeviceHandler) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
AssignResources.RegistryPath = RegistryPath;
|
|
AssignResources.DriverClassName = DriverClassName;
|
|
AssignResources.DriverObject = DriverObject;
|
|
AssignResources.AllocatedResources = AllocatedResources;
|
|
BufferSize = sizeof (AssignResources);
|
|
|
|
//
|
|
// Make synchrous DeviceControl request
|
|
//
|
|
|
|
Status = HalDeviceControl (
|
|
DeviceHandler,
|
|
DeviceObject,
|
|
BCTL_ASSIGN_SLOT_RESOURCES,
|
|
&AssignResources,
|
|
&BufferSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Free the reference to DeviceHandler
|
|
//
|
|
|
|
ObDereferenceObject (DeviceHandler);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PcipQueryBusSlots (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG BufferSize,
|
|
OUT PULONG SlotNumbers,
|
|
OUT PULONG ReturnedLength
|
|
)
|
|
{
|
|
PSINGLE_LIST_ENTRY Link;
|
|
PPCI_PORT PciPort;
|
|
PDEVICE_DATA DeviceData;
|
|
ULONG cnt;
|
|
|
|
PAGED_CODE ();
|
|
|
|
PciPort = PCIPORTDATA (BusHandler);
|
|
|
|
//
|
|
// Synchronize will new devices being added
|
|
//
|
|
|
|
ExAcquireFastMutex (&PcipMutex);
|
|
|
|
//
|
|
// Fill in returned buffer length, or what size buffer is needed
|
|
//
|
|
|
|
|
|
*ReturnedLength = PciPort->NoValidSlots * sizeof (ULONG);
|
|
if (BufferSize < *ReturnedLength) {
|
|
|
|
//
|
|
// Callers buffer is not large enough
|
|
//
|
|
|
|
ExReleaseFastMutex (&PcipMutex);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Return caller all the possible slot number
|
|
//
|
|
|
|
cnt = 0;
|
|
for (Link = PciPort->ValidSlots.Next; Link; Link = Link->Next) {
|
|
DeviceData = CONTAINING_RECORD (Link, DEVICE_DATA, Next);
|
|
if (DeviceData->Valid) {
|
|
cnt += 1;
|
|
*(SlotNumbers++) = DeviceDataSlot(DeviceData);
|
|
}
|
|
}
|
|
|
|
*ReturnedLength = cnt * sizeof (ULONG);
|
|
ExReleaseFastMutex (&PcipMutex);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PcipDeviceControl (
|
|
IN PHAL_DEVICE_CONTROL_CONTEXT Context
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG ControlCode;
|
|
ULONG Junk;
|
|
PULONG BufferLength;
|
|
PLIST_ENTRY OldTail;
|
|
BOOLEAN UseWorker;
|
|
|
|
for (i=0; PcipControl[i].ControlHandler; i++) {
|
|
if (PcipControl[i].ControlCode == Context->DeviceControl.ControlCode) {
|
|
|
|
//
|
|
// Found DeviceControl handler
|
|
//
|
|
|
|
Context->ContextControlHandler = (ULONG) (PcipControl + i);
|
|
|
|
//
|
|
// Verify callers buffer is the min required length
|
|
//
|
|
|
|
|
|
if (*Context->DeviceControl.BufferLength < PcipControl[i].MinBuffer) {
|
|
Context->DeviceControl.Status = STATUS_BUFFER_TOO_SMALL;
|
|
*Context->DeviceControl.BufferLength = PcipControl[i].MinBuffer;
|
|
HalCompleteDeviceControl (Context);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (KeGetCurrentIrql() < DISPATCH_LEVEL ||
|
|
(Context->DeviceControl.ControlCode == BCTL_SET_POWER &&
|
|
*((PPOWER_STATE) Context->DeviceControl.Buffer) == PowerUp)){
|
|
|
|
//
|
|
// All slot controls, expect a power up request, may touch
|
|
// paged code or data. If the current irql is low enough or
|
|
// this is a power up go dispatch now; otherwise, queue the
|
|
// request to a worker thread.
|
|
//
|
|
|
|
PcipDispatchControl (Context);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Enqueue to worker thread
|
|
//
|
|
|
|
ExInterlockedInsertTailList (
|
|
&PcipControlWorkerList,
|
|
(PLIST_ENTRY) &Context->ContextWorkQueue,
|
|
&PcipSpinlock
|
|
);
|
|
|
|
//
|
|
// Make sure worker is requested
|
|
//
|
|
|
|
PcipStartWorker ();
|
|
}
|
|
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Unkown control code
|
|
//
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PDEVICE_HANDLER_OBJECT
|
|
PcipReferenceDeviceHandler (
|
|
IN struct _BUS_HANDLER *BusHandler,
|
|
IN struct _BUS_HANDLER *RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber
|
|
)
|
|
{
|
|
PDEVICE_DATA DeviceData;
|
|
PDEVICE_HANDLER_OBJECT DeviceHandler;
|
|
PPCI_PORT PciPort;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ExAcquireFastMutex (&PcipMutex);
|
|
|
|
PciPort = PCIPORTDATA(BusHandler);
|
|
DeviceData = PcipFindDeviceData (PciPort, SlotNumber);
|
|
DeviceHandler = NULL;
|
|
if (DeviceData) {
|
|
DeviceHandler = DeviceData2DeviceHandler (DeviceData);
|
|
Status = ObReferenceObjectByPointer(
|
|
DeviceHandler,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
*IoDeviceHandlerObjectType,
|
|
KernelMode
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DeviceHandler = NULL;
|
|
}
|
|
}
|
|
|
|
ExReleaseFastMutex (&PcipMutex);
|
|
return DeviceHandler;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcipHibernateBus (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler
|
|
)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcipResumeBus (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler
|
|
)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
VOID
|
|
PcipSuspendNotification (
|
|
IN PVOID CallbackContext,
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
switch ((ULONG) Argument1) {
|
|
case 0:
|
|
//
|
|
// Lock code down which might be needed to perform a suspend
|
|
//
|
|
|
|
ASSERT (PciCodeLock == NULL);
|
|
PciCodeLock = MmLockPagableCodeSection (&PcipHibernateBus);
|
|
break;
|
|
|
|
case 1:
|
|
//
|
|
// Release the code lock
|
|
//
|
|
|
|
MmUnlockPagableImageSection (PciCodeLock);
|
|
PciCodeLock = NULL;
|
|
break;
|
|
}
|
|
}
|