901 lines
21 KiB
C
901 lines
21 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
routintf.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the "Pci Interrupt Routing" interfaces supported
|
|||
|
by the PCI driver.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jake Oshins (jakeo) 19-Jul-1997
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Elliot Shmukler (t-ellios) 7-15-1998 Modified the PCI routing interface to support
|
|||
|
MSI capable devices.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pcip.h"
|
|||
|
|
|||
|
#define MSI_SIMULATE 0
|
|||
|
|
|||
|
//
|
|||
|
// Prototypes for routines exposed only thru the "interface"
|
|||
|
// mechanism.
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
routeintrf_Constructor(
|
|||
|
PVOID DeviceExtension,
|
|||
|
PVOID PciInterface,
|
|||
|
PVOID InterfaceSpecificData,
|
|||
|
USHORT Version,
|
|||
|
USHORT Size,
|
|||
|
PINTERFACE InterfaceReturn
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
routeintrf_Initializer(
|
|||
|
IN PPCI_ARBITER_INSTANCE Instance
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
routeintrf_Reference(
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
routeintrf_Dereference(
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciGetInterruptRoutingInfoEx(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
OUT ULONG *Bus,
|
|||
|
OUT ULONG *PciSlot,
|
|||
|
OUT UCHAR *InterruptLine,
|
|||
|
OUT UCHAR *InterruptPin,
|
|||
|
OUT UCHAR *ClassCode,
|
|||
|
OUT UCHAR *SubClassCode,
|
|||
|
OUT PDEVICE_OBJECT *ParentPdo,
|
|||
|
OUT ROUTING_TOKEN *RoutingToken,
|
|||
|
OUT UCHAR *Flags
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciSetRoutingToken(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
IN PROUTING_TOKEN RoutingToken
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciSetRoutingTokenEx(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
IN PROUTING_TOKEN RoutingToken
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PciUpdateInterruptLine(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
IN UCHAR Line
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciGetInterruptRoutingInfo(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
OUT ULONG *Bus,
|
|||
|
OUT ULONG *PciSlot,
|
|||
|
OUT UCHAR *InterruptLine,
|
|||
|
OUT UCHAR *InterruptPin,
|
|||
|
OUT UCHAR *ClassCode,
|
|||
|
OUT UCHAR *SubClassCode,
|
|||
|
OUT PDEVICE_OBJECT *ParentPdo,
|
|||
|
OUT ROUTING_TOKEN *RoutingToken
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciSetLegacyDeviceToken(
|
|||
|
IN PDEVICE_OBJECT LegacyDO,
|
|||
|
IN PROUTING_TOKEN RoutingToken
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciFindLegacyDevice(
|
|||
|
IN PDEVICE_OBJECT LegacyDO,
|
|||
|
OUT ULONG *Bus,
|
|||
|
OUT ULONG *PciSlot,
|
|||
|
OUT UCHAR *InterruptLine,
|
|||
|
OUT UCHAR *InterruptPin,
|
|||
|
OUT UCHAR *ClassCode,
|
|||
|
OUT UCHAR *SubClassCode,
|
|||
|
OUT PDEVICE_OBJECT *ParentPdo,
|
|||
|
OUT ROUTING_TOKEN *RoutingToken
|
|||
|
);
|
|||
|
|
|||
|
typedef struct {
|
|||
|
PCI_SECONDARY_EXTENSION ExtensionHeader;
|
|||
|
ROUTING_TOKEN RoutingToken;
|
|||
|
} ROUTING_EXTENSION, *PROUTING_EXTENSION;
|
|||
|
|
|||
|
//
|
|||
|
// Define the Pci Routing interface "Interface" structure.
|
|||
|
//
|
|||
|
|
|||
|
PCI_INTERFACE PciRoutingInterface = {
|
|||
|
&GUID_INT_ROUTE_INTERFACE_STANDARD, // InterfaceType
|
|||
|
sizeof(INT_ROUTE_INTERFACE_STANDARD), // MinSize
|
|||
|
PCI_INT_ROUTE_INTRF_STANDARD_VER, // MinVersion
|
|||
|
PCI_INT_ROUTE_INTRF_STANDARD_VER, // MaxVersion
|
|||
|
PCIIF_FDO, // Flags
|
|||
|
0, // ReferenceCount
|
|||
|
PciInterface_IntRouteHandler, // Signature
|
|||
|
routeintrf_Constructor, // Constructor
|
|||
|
routeintrf_Initializer // Instance Initializer
|
|||
|
};
|
|||
|
|
|||
|
PLEGACY_DEVICE PciLegacyDeviceHead = NULL;
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, routeintrf_Constructor)
|
|||
|
#pragma alloc_text(PAGE, routeintrf_Initializer)
|
|||
|
#pragma alloc_text(PAGE, routeintrf_Reference)
|
|||
|
#pragma alloc_text(PAGE, PciGetInterruptRoutingInfo)
|
|||
|
#pragma alloc_text(PAGE, PciGetInterruptRoutingInfoEx)
|
|||
|
#pragma alloc_text(PAGE, PciSetRoutingToken)
|
|||
|
#pragma alloc_text(PAGE, PciSetRoutingTokenEx)
|
|||
|
#pragma alloc_text(PAGE, PciFindLegacyDevice)
|
|||
|
#pragma alloc_text(PAGE, PciCacheLegacyDeviceRouting)
|
|||
|
#pragma alloc_text(PAGE, PciUpdateInterruptLine)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
routeintrf_Reference(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
routeintrf_Constructor(
|
|||
|
PVOID DeviceExtension,
|
|||
|
PVOID PciInterface,
|
|||
|
PVOID InterfaceSpecificData,
|
|||
|
USHORT Version,
|
|||
|
USHORT Size,
|
|||
|
PINTERFACE InterfaceReturn
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize the BUS_INTERFACE_STANDARD fields.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PciInterface Pointer to the PciInterface record for this
|
|||
|
interface type.
|
|||
|
InterfaceSpecificData
|
|||
|
|
|||
|
InterfaceReturn
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PINT_ROUTE_INTERFACE_STANDARD standard = (PINT_ROUTE_INTERFACE_STANDARD)InterfaceReturn;
|
|||
|
|
|||
|
switch(Version)
|
|||
|
{
|
|||
|
case PCI_INT_ROUTE_INTRF_STANDARD_VER:
|
|||
|
standard->GetInterruptRouting = PciGetInterruptRoutingInfoEx;
|
|||
|
standard->SetInterruptRoutingToken = PciSetRoutingTokenEx;
|
|||
|
standard->UpdateInterruptLine = PciUpdateInterruptLine;
|
|||
|
break;
|
|||
|
default:
|
|||
|
return STATUS_NOINTERFACE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
standard->Size = sizeof( INT_ROUTE_INTERFACE_STANDARD );
|
|||
|
standard->Version = Version;
|
|||
|
standard->Context = DeviceExtension;
|
|||
|
standard->InterfaceReference = routeintrf_Reference;
|
|||
|
standard->InterfaceDereference = routeintrf_Reference;
|
|||
|
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
routeintrf_Initializer(
|
|||
|
IN PPCI_ARBITER_INSTANCE Instance
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
For bus interface, does nothing, shouldn't actually be called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Instance Pointer to the PDO extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the status of this operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ASSERTMSG("PCI routeintrf_Initializer, unexpected call.", 0);
|
|||
|
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciGetInterruptRoutingInfo(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
OUT ULONG *Bus,
|
|||
|
OUT ULONG *PciSlot,
|
|||
|
OUT UCHAR *InterruptLine,
|
|||
|
OUT UCHAR *InterruptPin,
|
|||
|
OUT UCHAR *ClassCode,
|
|||
|
OUT UCHAR *SubClassCode,
|
|||
|
OUT PDEVICE_OBJECT *ParentPdo,
|
|||
|
OUT ROUTING_TOKEN *RoutingToken
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Each PCI device in the system is connected to some
|
|||
|
interrupt pin. And in order to figure out which interrupt
|
|||
|
that device may trigger, an IRQ arbiter must have this
|
|||
|
information. This interface gathers all such information
|
|||
|
for the arbiter.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Pdo - Device object that the arbiter needs to inquire about
|
|||
|
Bus - Number of the PCI bus in question
|
|||
|
PciSlot - Slot/Function that corresponds to this device
|
|||
|
InterruptLine - Contents of the device's interrupt line register
|
|||
|
InterruptPin - Contents of the device's interrupt pin register
|
|||
|
ClassCode - type of device
|
|||
|
SubClassCode - sub-type of device
|
|||
|
ParentPdo - PDO of parent PCI bus
|
|||
|
RoutingToken - blob of data
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - this is a PCI device and all the fields have been filled
|
|||
|
STATUS_NOT_FOUND - to the knowledge of the PCI driver, this is not a PCI device
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PROUTING_EXTENSION RoutingExtension;
|
|||
|
PPCI_PDO_EXTENSION PdoExt = (PPCI_PDO_EXTENSION)Pdo->DeviceExtension;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
ASSERT(Bus);
|
|||
|
ASSERT(PciSlot);
|
|||
|
ASSERT(InterruptLine);
|
|||
|
ASSERT(InterruptPin);
|
|||
|
ASSERT(ClassCode);
|
|||
|
ASSERT(SubClassCode);
|
|||
|
ASSERT(ParentPdo);
|
|||
|
ASSERT(RoutingToken);
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if this is a legacy PCI device that
|
|||
|
// we are tracking.
|
|||
|
//
|
|||
|
|
|||
|
status = PciFindLegacyDevice(Pdo,
|
|||
|
Bus,
|
|||
|
PciSlot,
|
|||
|
InterruptLine,
|
|||
|
InterruptPin,
|
|||
|
ClassCode,
|
|||
|
SubClassCode,
|
|||
|
ParentPdo,
|
|||
|
RoutingToken);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
//
|
|||
|
// If so, the fields have been filled in already.
|
|||
|
//
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Verify that this PDO actually belongs to us.
|
|||
|
//
|
|||
|
if (!PdoExt) {
|
|||
|
return STATUS_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Verify that it is actually a PDO.
|
|||
|
//
|
|||
|
if (PdoExt->ExtensionType != PciPdoExtensionType) {
|
|||
|
return STATUS_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
*Bus = PCI_PARENT_FDOX(PdoExt)->BaseBus;
|
|||
|
*PciSlot = PdoExt->Slot.u.AsULONG;
|
|||
|
*InterruptLine = PdoExt->RawInterruptLine;
|
|||
|
*InterruptPin = PdoExt->InterruptPin;
|
|||
|
*ClassCode = PdoExt->BaseClass;
|
|||
|
*SubClassCode = PdoExt->SubClass;
|
|||
|
*ParentPdo = PCI_PARENT_PDO(PdoExt);
|
|||
|
|
|||
|
RoutingExtension = PciFindSecondaryExtension(PdoExt,
|
|||
|
PciInterface_IntRouteHandler);
|
|||
|
|
|||
|
if (RoutingExtension) {
|
|||
|
|
|||
|
*RoutingToken = RoutingExtension->RoutingToken;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
RoutingToken->LinkNode = 0;
|
|||
|
RoutingToken->StaticVector = 0;
|
|||
|
RoutingToken->Flags = 0;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciGetInterruptRoutingInfoEx(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
OUT ULONG *Bus,
|
|||
|
OUT ULONG *PciSlot,
|
|||
|
OUT UCHAR *InterruptLine,
|
|||
|
OUT UCHAR *InterruptPin,
|
|||
|
OUT UCHAR *ClassCode,
|
|||
|
OUT UCHAR *SubClassCode,
|
|||
|
OUT PDEVICE_OBJECT *ParentPdo,
|
|||
|
OUT ROUTING_TOKEN *RoutingToken,
|
|||
|
OUT UCHAR *Flags
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Wrapper for PciGetInterruptroutingInfo that sets
|
|||
|
the Flags parameter to indicate MSI-capable PCI devices.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Mostly, same as PciGetInterruptRoutingInfo.
|
|||
|
|
|||
|
Flags receives device-specific flags such as whether the device
|
|||
|
supports MSI.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Same as PciGetInterruptRoutingInfo.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PPCI_PDO_EXTENSION PdoExt = (PPCI_PDO_EXTENSION)Pdo->DeviceExtension;
|
|||
|
|
|||
|
// Call the real function
|
|||
|
|
|||
|
status = PciGetInterruptRoutingInfo(Pdo,
|
|||
|
Bus,
|
|||
|
PciSlot,
|
|||
|
InterruptLine,
|
|||
|
InterruptPin,
|
|||
|
ClassCode,
|
|||
|
SubClassCode,
|
|||
|
ParentPdo,
|
|||
|
RoutingToken);
|
|||
|
|
|||
|
*Flags = 0;
|
|||
|
|
|||
|
|
|||
|
#if MSI_SUPPORTED
|
|||
|
|
|||
|
if (NT_SUCCESS(status)
|
|||
|
|
|||
|
#if !MSI_SIMULATE
|
|||
|
&& PdoExt->CapableMSI
|
|||
|
#endif
|
|||
|
) {
|
|||
|
|
|||
|
|
|||
|
// MSI device?
|
|||
|
*Flags = PCI_MSI_ROUTING;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endif // MSI_SUPPORTED
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciSetRoutingToken(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
IN PROUTING_TOKEN RoutingToken
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine stores a blob of data associated with this
|
|||
|
PCI device. This job is foisted off on the PCI driver because
|
|||
|
the PCI driver has a one-to-one correspondence between useful
|
|||
|
data structures and PCI devices.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Pdo - Device object that the IRQ arbiter is working with
|
|||
|
|
|||
|
RoutingToken - Blob of data that the IRQ arbiter wants to associate with
|
|||
|
the PCI device.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the status of this operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PROUTING_EXTENSION RoutingExtension;
|
|||
|
PPCI_PDO_EXTENSION PdoExt = (PPCI_PDO_EXTENSION)Pdo->DeviceExtension;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// Check first to see if this is a legacy PCI device.
|
|||
|
//
|
|||
|
|
|||
|
status = PciSetLegacyDeviceToken(Pdo, RoutingToken);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This isn't in our list of legacy devices. So it must be
|
|||
|
// a PCI PDO.
|
|||
|
//
|
|||
|
|
|||
|
#if DBG
|
|||
|
RoutingExtension = PciFindSecondaryExtension(PdoExt,
|
|||
|
PciInterface_IntRouteHandler);
|
|||
|
|
|||
|
if (RoutingExtension != NULL) {
|
|||
|
DbgPrint("PCI: *** redundant PCI routing extesion being created ***\n");
|
|||
|
}
|
|||
|
ASSERT(RoutingExtension == NULL);
|
|||
|
#endif
|
|||
|
|
|||
|
RoutingExtension = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION,
|
|||
|
sizeof(ROUTING_EXTENSION));
|
|||
|
|
|||
|
if (!RoutingExtension) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
RoutingExtension->RoutingToken = *RoutingToken;
|
|||
|
|
|||
|
PciLinkSecondaryExtension(PdoExt,
|
|||
|
RoutingExtension,
|
|||
|
PciInterface_IntRouteHandler,
|
|||
|
NULL);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciSetRoutingTokenEx(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
IN PROUTING_TOKEN RoutingToken
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
MSI-aware wrapper for PciSetRoutingToken.
|
|||
|
|
|||
|
This function will intercept MSI routing tokens (as indicated by
|
|||
|
the PCI_MSI_ROUTING flag) and store the MSI routing information
|
|||
|
in the PDO extension without caching the token in a secondary extension.
|
|||
|
|
|||
|
Non-MSI routing tokens will be passed to PciSetRoutingToken.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Same as PciSetRoutingToken.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Same as PciSetRoutingToken.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PPCI_PDO_EXTENSION PdoExt = (PPCI_PDO_EXTENSION)Pdo->DeviceExtension;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
#if MSI_SUPPORTED
|
|||
|
|
|||
|
if(
|
|||
|
|
|||
|
#if !MSI_SIMULATE
|
|||
|
PdoExt->CapableMSI &&
|
|||
|
#endif
|
|||
|
|
|||
|
(RoutingToken->Flags & PCI_MSI_ROUTING))
|
|||
|
{
|
|||
|
|
|||
|
// MSI token
|
|||
|
|
|||
|
PciDebugPrint(PciDbgInformative,"PCI: MSI Resources Received for Device %08x\n", Pdo);
|
|||
|
|
|||
|
#ifdef DBG
|
|||
|
|
|||
|
// have we received a new resource assignment?
|
|||
|
|
|||
|
if ((PdoExt->MsiInfo.MessageAddress != (ULONG_PTR)RoutingToken->LinkNode)
|
|||
|
|| (PdoExt->MsiInfo.MessageData != (USHORT)RoutingToken->StaticVector)) {
|
|||
|
|
|||
|
PciDebugPrint(PciDbgPrattling,"PCI: Device %08x will be reprogrammed with Message = %ld @%p\n",
|
|||
|
Pdo, RoutingToken->StaticVector, RoutingToken->LinkNode);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
// Store MSI info in PdoExt.
|
|||
|
|
|||
|
PdoExt->MsiInfo.MessageAddress = (ULONG_PTR)RoutingToken->LinkNode;
|
|||
|
PdoExt->MsiInfo.MessageData = (USHORT)RoutingToken->StaticVector;
|
|||
|
}
|
|||
|
else
|
|||
|
|
|||
|
#endif // MSI_SUPPORTED
|
|||
|
|
|||
|
{
|
|||
|
// Call the original function on non-MSI tokens.
|
|||
|
|
|||
|
status = PciSetRoutingToken(Pdo, RoutingToken);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// NT 5.0 has to support non-PnP 4.0-style device drivers. And
|
|||
|
// this driver doesn't create the device objects associated with
|
|||
|
// a PCI device when its driver is 4.0-style. It does, however
|
|||
|
// get a chance to look at those objects when the driver calls
|
|||
|
// HalAssignSlotResources. This collection of functions tracks
|
|||
|
// these foreign device objects.
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciFindLegacyDevice(
|
|||
|
IN PDEVICE_OBJECT LegacyDO,
|
|||
|
OUT ULONG *Bus,
|
|||
|
OUT ULONG *PciSlot,
|
|||
|
OUT UCHAR *InterruptLine,
|
|||
|
OUT UCHAR *InterruptPin,
|
|||
|
OUT UCHAR *ClassCode,
|
|||
|
OUT UCHAR *SubClassCode,
|
|||
|
OUT PDEVICE_OBJECT *ParentPdo,
|
|||
|
OUT ROUTING_TOKEN *RoutingToken
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function returns all the routing data for a legacy
|
|||
|
device object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the status of this operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLEGACY_DEVICE legacyDev = PciLegacyDeviceHead;
|
|||
|
NTSTATUS status = STATUS_NOT_FOUND;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
while (legacyDev) {
|
|||
|
|
|||
|
if (legacyDev->LegacyDeviceObject == LegacyDO) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} else if (legacyDev->Bus == *Bus && legacyDev->PciSlot == *PciSlot) {
|
|||
|
|
|||
|
if (legacyDev->LegacyDeviceObject == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Cache the LegacyDO in case we matched on the bus and slot info.
|
|||
|
//
|
|||
|
|
|||
|
legacyDev->LegacyDeviceObject = LegacyDO;
|
|||
|
break;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
PciDebugPrint(PciDbgAlways, "Two PDOs (Legacy = %08x, Pnp = %08x) for device on bus %08x, slot %08x\n", legacyDev->LegacyDeviceObject, LegacyDO, *Bus, *PciSlot);
|
|||
|
ASSERT(legacyDev->LegacyDeviceObject != NULL);
|
|||
|
legacyDev = NULL;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
legacyDev = (PLEGACY_DEVICE)legacyDev->List.Next;
|
|||
|
}
|
|||
|
|
|||
|
if (legacyDev) {
|
|||
|
|
|||
|
*Bus = legacyDev->Bus;
|
|||
|
*PciSlot = legacyDev->PciSlot;
|
|||
|
*InterruptLine = legacyDev->InterruptLine;
|
|||
|
*InterruptPin = legacyDev->InterruptPin;
|
|||
|
*ClassCode = legacyDev->ClassCode;
|
|||
|
*SubClassCode = legacyDev->SubClassCode;
|
|||
|
*ParentPdo = legacyDev->ParentPdo;
|
|||
|
*RoutingToken = legacyDev->RoutingToken;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciCacheLegacyDeviceRouting(
|
|||
|
IN PDEVICE_OBJECT LegacyDO,
|
|||
|
IN ULONG Bus,
|
|||
|
IN ULONG PciSlot,
|
|||
|
IN UCHAR InterruptLine,
|
|||
|
IN UCHAR InterruptPin,
|
|||
|
IN UCHAR ClassCode,
|
|||
|
IN UCHAR SubClassCode,
|
|||
|
IN PDEVICE_OBJECT ParentPdo,
|
|||
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|||
|
OUT PDEVICE_OBJECT *OldLegacyDO
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function stores all the routing data for a legacy
|
|||
|
device object, except the RoutingToken. Caller needs to acquire
|
|||
|
PciGlobalLock before calling this function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the status of this operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLEGACY_DEVICE legacyDev = PciLegacyDeviceHead;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
while (legacyDev) {
|
|||
|
|
|||
|
if (Bus == legacyDev->Bus && PciSlot == legacyDev->PciSlot) {
|
|||
|
if (legacyDev->LegacyDeviceObject == LegacyDO) {
|
|||
|
|
|||
|
//
|
|||
|
// This device is already in the list.
|
|||
|
//
|
|||
|
|
|||
|
if (OldLegacyDO) {
|
|||
|
|
|||
|
*OldLegacyDO = LegacyDO;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
|
|||
|
PDEVICE_OBJECT oldDO;
|
|||
|
|
|||
|
//
|
|||
|
// We are overwriting an existing entry.
|
|||
|
//
|
|||
|
|
|||
|
oldDO = legacyDev->LegacyDeviceObject;
|
|||
|
legacyDev->LegacyDeviceObject = LegacyDO;
|
|||
|
|
|||
|
if (OldLegacyDO) {
|
|||
|
|
|||
|
*OldLegacyDO = oldDO;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
legacyDev = (PLEGACY_DEVICE)legacyDev->List.Next;
|
|||
|
}
|
|||
|
|
|||
|
legacyDev = ExAllocatePool(PagedPool,
|
|||
|
sizeof(LEGACY_DEVICE));
|
|||
|
|
|||
|
if (!legacyDev) {
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(legacyDev, sizeof(LEGACY_DEVICE));
|
|||
|
|
|||
|
legacyDev->LegacyDeviceObject = LegacyDO;
|
|||
|
legacyDev->Bus = Bus;
|
|||
|
legacyDev->PciSlot = PciSlot;
|
|||
|
legacyDev->InterruptLine = InterruptLine;
|
|||
|
legacyDev->InterruptPin = InterruptPin;
|
|||
|
legacyDev->ClassCode = ClassCode;
|
|||
|
legacyDev->SubClassCode = SubClassCode;
|
|||
|
legacyDev->ParentPdo = ParentPdo;
|
|||
|
legacyDev->PdoExtension = PdoExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Push this one onto the list.
|
|||
|
//
|
|||
|
|
|||
|
legacyDev->List.Next = (PSINGLE_LIST_ENTRY)PciLegacyDeviceHead;
|
|||
|
PciLegacyDeviceHead = legacyDev;
|
|||
|
|
|||
|
if (OldLegacyDO) {
|
|||
|
|
|||
|
*OldLegacyDO = LegacyDO;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PciSetLegacyDeviceToken(
|
|||
|
IN PDEVICE_OBJECT LegacyDO,
|
|||
|
IN PROUTING_TOKEN RoutingToken
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function stores the RoutingToken.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Returns the status of this operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLEGACY_DEVICE legacyDev = PciLegacyDeviceHead;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
while (legacyDev) {
|
|||
|
|
|||
|
if (legacyDev->LegacyDeviceObject == LegacyDO) {
|
|||
|
|
|||
|
//
|
|||
|
// Found it. Copy the token into the structure.
|
|||
|
//
|
|||
|
|
|||
|
legacyDev->RoutingToken = *RoutingToken;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
legacyDev = (PLEGACY_DEVICE)legacyDev->List.Next;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PciUpdateInterruptLine(
|
|||
|
IN PDEVICE_OBJECT Pdo,
|
|||
|
IN UCHAR Line
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PPCI_PDO_EXTENSION pdoExt;
|
|||
|
PLEGACY_DEVICE legacyDev = PciLegacyDeviceHead;
|
|||
|
PCI_COMMON_HEADER header;
|
|||
|
PPCI_COMMON_CONFIG biosConfig = (PPCI_COMMON_CONFIG) &header;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Work out if this is a legacy PDO or not
|
|||
|
//
|
|||
|
|
|||
|
while (legacyDev) {
|
|||
|
|
|||
|
if (legacyDev->LegacyDeviceObject == Pdo) {
|
|||
|
|
|||
|
//
|
|||
|
// Found it.
|
|||
|
//
|
|||
|
|
|||
|
pdoExt = legacyDev->PdoExtension;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
legacyDev = (PLEGACY_DEVICE)legacyDev->List.Next;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (legacyDev == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Oh well it must be a PCI pdo
|
|||
|
//
|
|||
|
|
|||
|
pdoExt = Pdo->DeviceExtension;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT_PCI_PDO_EXTENSION(pdoExt);
|
|||
|
|
|||
|
//
|
|||
|
// Now we can update the hardware and our internal state!
|
|||
|
//
|
|||
|
|
|||
|
pdoExt->RawInterruptLine = pdoExt->AdjustedInterruptLine = Line;
|
|||
|
|
|||
|
PciWriteDeviceConfig(pdoExt,
|
|||
|
&Line,
|
|||
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.InterruptLine),
|
|||
|
sizeof(Line)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Finally refresh the config space stored in the registry
|
|||
|
//
|
|||
|
|
|||
|
status = PciGetBiosConfig(pdoExt, biosConfig);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(status));
|
|||
|
|
|||
|
if (NT_SUCCESS(status)
|
|||
|
&& biosConfig->u.type0.InterruptLine != Line) {
|
|||
|
|
|||
|
biosConfig->u.type0.InterruptLine = Line;
|
|||
|
|
|||
|
status = PciSaveBiosConfig(pdoExt, biosConfig);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(status));
|
|||
|
|
|||
|
}
|
|||
|
}
|