windows-nt/Source/XPSP1/NT/base/busdrv/acpi/driver/nt/table.c
2020-09-26 16:20:57 +08:00

622 lines
13 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
table.c
Abstract:
All the function related to actually loading an ACPI table
are included herein.
This, however, is mostly bookkeeping since the actual mechanics
of creating device extensions, and the name space tree are
handled elsewhere
Author:
Stephane Plante (splante)
Environment:
Kernel Mode Only
Revision History:
03/22/00 - Created (from code in callback.c)
--*/
#include "pch.h"
NTSTATUS
ACPITableLoad(
VOID
)
/*++
Routine Description:
This routine is called when the AML interpreter has finished loading
a Differentiated Data Block
Arguments:
None
Return Value:
NTSTATUS
--*/
{
BOOLEAN runRootIni = FALSE;
KIRQL oldIrql;
NTSTATUS status;
PDEVICE_EXTENSION fixedButtonExtension = NULL;
PNSOBJ iniObject;
PNSOBJ nsObject;
//
// At this point, we should do everything that we need to do once the
// name space has been loaded. Note that we need to make sure that we
// only do those things once...
//
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
//
// We need the ACPI object for the _SB tree
//
status = AMLIGetNameSpaceObject( "\\_SB", NULL, &nsObject, 0 );
if (!NT_SUCCESS(status)) {
//
// Ooops. Failure
//
ACPIPrint( (
ACPI_PRINT_CRITICAL,
"ACPICallBackLoadUnloadDDB: No SB Object!\n"
) );
ACPIInternalError( ACPI_CALLBACK );
return STATUS_SUCCESS;
}
//
// Make sure that the root device extension's object points to the correct
// thing. We only want to run through this code path once...
//
if (RootDeviceExtension->AcpiObject == NULL) {
runRootIni = TRUE;
InterlockedIncrement( &(RootDeviceExtension->ReferenceCount) );
RootDeviceExtension->AcpiObject = nsObject;
nsObject->Context = RootDeviceExtension;
//
// Now, enumerate the fixed button
//
status = ACPIBuildFixedButtonExtension(
RootDeviceExtension,
&fixedButtonExtension
);
if (NT_SUCCESS(status) &&
fixedButtonExtension != NULL) {
//
// Incremement the reference count on the node. We do this because
// we are going to be doing work (which will take a long time
// to complete, anyways), and we don't want to hold the lock for that
// entire time. If we incr the reference count, then we guarantee that
// no one can come along and kick the feet out from underneath us
//
InterlockedIncrement( &(fixedButtonExtension->ReferenceCount) );
}
}
//
// We now want to run the _INI through the entire tree, starting at
// the _SB
//
status = ACPIBuildRunMethodRequest(
RootDeviceExtension,
NULL,
NULL,
PACKED_INI,
(RUN_REQUEST_CHECK_STATUS | RUN_REQUEST_RECURSIVE | RUN_REQUEST_MARK_INI),
FALSE
);
if (!NT_SUCCESS(status)) {
ACPIInternalError( ACPI_CALLBACK );
}
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
//
// We also need to run the _INI method off of the root name space entry
//
if (runRootIni) {
iniObject = ACPIAmliGetNamedChild( nsObject->pnsParent, PACKED_INI );
if (iniObject) {
AMLINestAsyncEvalObject(
iniObject,
NULL,
0,
NULL,
NULL,
NULL
);
}
}
//
// We need a synchronization point after we finish running the
// DPC engine. We want to be able to move anything in the Delayed
// Power Queue over to the Power DPC engine
//
status = ACPIBuildSynchronizationRequest(
RootDeviceExtension,
ACPITableLoadCallBack,
NULL,
&AcpiBuildDeviceList,
FALSE
);
if (!NT_SUCCESS(status)) {
ACPIInternalError( ACPI_CALLBACK );
}
//
// We need to hold this spinlock
//
KeAcquireSpinLock( &AcpiBuildQueueLock, &oldIrql );
//
// Do we need to run the DPC?
//
if (!AcpiBuildDpcRunning) {
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0);
}
//
// Done with the lock
//
KeReleaseSpinLock( &AcpiBuildQueueLock, oldIrql );
//
// Done
//
return STATUS_SUCCESS;
}
VOID
ACPITableLoadCallBack(
IN PVOID BuildContext,
IN PVOID Context,
IN NTSTATUS Status
)
/*++
Routine Description:
This routine is called when we have emptied all of the elements
within the AcpiBuildDeviceList. This is a good time to move items
from the AcpiPowerDelayedQueueList to the AcpiPowerQueueList.
Arguments:
BuildContext - Not used (it is the RootDeviceExtension)
Context - NULL
Status - Status of the operation
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER( BuildContext );
UNREFERENCED_PARAMETER( Context );
UNREFERENCED_PARAMETER( Status );
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
//
// We want to rebuilt the device based GPE mask here, so
// we need the following locks
//
KeAcquireSpinLockAtDpcLevel( &AcpiDeviceTreeLock );
KeAcquireSpinLockAtDpcLevel( &GpeTableLock );
//
// Now, we need to walk the device namespace and find which events
// are special, which are wake events, and which are run-time events
// As a matter of practical theory, its not possible for there to
// be a _PRW on the root device extension, so we should be safely
// able to walk only the Root's children and thereon
//
ACPIGpeBuildWakeMasks(RootDeviceExtension);
//
// We don't need these particular spin locks anymore
//
KeReleaseSpinLockFromDpcLevel( &GpeTableLock );
KeReleaseSpinLockFromDpcLevel( &AcpiDeviceTreeLock );
//
// We need the power lock to touch these Power Queues
//
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
//
// If we there are items on the delayed list, we need to put them
// on the main list
//
if (!IsListEmpty( &AcpiPowerDelayedQueueList ) ) {
//
// Move the list
//
ACPIInternalMoveList(
&AcpiPowerDelayedQueueList,
&AcpiPowerQueueList
);
//
// Schedule the DPC, if necessary
///
if (!AcpiPowerDpcRunning) {
KeInsertQueueDpc( &AcpiPowerDpc, 0, 0 );
}
}
//
// Done with the lock
//
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
}
NTSTATUS
EXPORT
ACPITableNotifyFreeObject(
ULONG Event,
PVOID Context,
ULONG ObjectType
)
/*++
Routine Description:
This routine is called when interpreter tells us that an
object has been freed
Arguments:
Event - the step in unload
Object - the object being unloaded
ObjectType - the type of the object
--*/
{
LONG oldReferenceCount;
PDEVICE_EXTENSION deviceExtension;
PDEVICE_EXTENSION parentExtension;
PKIRQL oldIrql;
PNSOBJ object;
//
// Start case
//
if (Event == DESTROYOBJ_START) {
ACPIPrint( (
ACPI_PRINT_CRITICAL,
"Unloading: Start\n"
) );
oldIrql = (PKIRQL) Context;
KeAcquireSpinLock( &AcpiDeviceTreeLock, oldIrql );
return STATUS_SUCCESS;
}
if (Event == DESTROYOBJ_END) {
ACPIPrint( (
ACPI_PRINT_CRITICAL,
"Unloading: End\n"
) );
oldIrql = (PKIRQL) Context;
KeReleaseSpinLock( &AcpiDeviceTreeLock, *oldIrql );
return STATUS_SUCCESS;
}
//
// At this point, we have a either a valid unload request or
// a bugcheck request
//
object = (PNSOBJ) Context;
//
// Let the world Know...
//
ACPIPrint( (
ACPI_PRINT_CRITICAL,
"%x: Unloading: %x %x %x\n",
(object ? object->Context : 0),
object,
ObjectType,
Event
) );
//
// Handle the bugcheck cases
//
if (Event == DESTROYOBJ_CHILD_NOT_FREED) {
KeBugCheckEx(
ACPI_BIOS_ERROR,
ACPI_TABLE_UNLOAD,
(ULONG_PTR) object,
0,
0
);
}
if (Event == DESTROYOBJ_BOGUS_PARENT) {
KeBugCheckEx(
ACPI_BIOS_ERROR,
ACPI_TABLE_UNLOAD,
(ULONG_PTR) object,
1,
0
);
}
//
// We only understand processors, thermal zones, and devices for right
// now, we will have to add power resources at a later point
//
if (ObjectType == OBJTYPE_POWERRES) {
return STATUS_SUCCESS;
}
//
// Grab the device extension, and make sure that one exists
//
deviceExtension = object->Context;
if (deviceExtension == NULL) {
//
// No device extension, so we can free this thing *now*
//
AMLIDestroyFreedObjs( object );
return STATUS_SUCCESS;
}
//
// Mark the extension as no longer existing
//
ACPIInternalUpdateFlags(
&(deviceExtension->Flags),
DEV_PROP_UNLOADING,
FALSE
);
//
// Does this device have a parent extension? It might not
// have an extension if the parent has been marked for removal
//
parentExtension = deviceExtension->ParentExtension;
if (parentExtension != NULL) {
//
// Mark the parent's relations as invalid
//
ACPIInternalUpdateFlags(
&(parentExtension->Flags),
DEV_PROP_INVALID_RELATIONS,
FALSE
);
}
//
// Finally, decrement the reference count on the device...
//
oldReferenceCount = InterlockedDecrement(
&(deviceExtension->ReferenceCount)
);
if (oldReferenceCount == 0) {
//
// Free this extension
//
ACPIInitDeleteDeviceExtension( deviceExtension );
}
//
// Done
//
return STATUS_SUCCESS;
}
NTSTATUS
ACPITableUnload(
VOID
)
/*++
Routine Description:
This routine is called after a table has been unloaded.
The purpose of this routine is to go out and issue the invalidate
device relations on all elements of the table whose children are
going away...
Arguments:
None
Return value:
NTSTATUS
--*/
{
KIRQL oldIrql;
PDEVICE_EXTENSION deviceExtension;
//
// We will need to hold the device tree lock for the following
//
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
//
// Check to see if we have to invalid the root's device extension?
//
deviceExtension = RootDeviceExtension;
if (deviceExtension && !(deviceExtension->Flags & DEV_TYPE_NOT_FOUND) ) {
if (deviceExtension->Flags & DEV_PROP_INVALID_RELATIONS) {
ACPIInternalUpdateFlags(
&(deviceExtension->Flags),
DEV_PROP_INVALID_RELATIONS,
TRUE
);
IoInvalidateDeviceRelations(
deviceExtension->PhysicalDeviceObject,
BusRelations
);
} else {
//
// Walk the namespace looking for bogus relations
//
ACPITableUnloadInvalidateRelations( deviceExtension );
}
}
//
// Done with the lock
//
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
//
// And with the function
//
return STATUS_SUCCESS;
}
NTSTATUS
ACPITableUnloadInvalidateRelations(
IN PDEVICE_EXTENSION DeviceExtension
)
/*++
Routine Description:
This recursive routine is called to walke the namespace and issue
the appropriate invalidates.
The device tree lock is owned during this call...
Arguments:
DeviceExtension - The device whose child extension we have to check
Return Value:
NTSTATUS
--*/
{
EXTENSIONLIST_ENUMDATA eled;
PDEVICE_EXTENSION childExtension;
//
// Setup the data structures that we will use to walk the
// device extension tree
//
ACPIExtListSetupEnum(
&eled,
&(DeviceExtension->ChildDeviceList),
NULL,
SiblingDeviceList,
WALKSCHEME_NO_PROTECTION
);
//
// Look at all the children of the current device extension
//
for (childExtension = ACPIExtListStartEnum( &eled) ;
ACPIExtListTestElement( &eled, TRUE);
childExtension = ACPIExtListEnumNext( &eled) ) {
//
// Does this object have any device objects?
//
if (!(childExtension->Flags & DEV_TYPE_NOT_FOUND) ) {
continue;
}
//
// Do we have to invalidate this object's relations?
//
if (childExtension->Flags & DEV_PROP_INVALID_RELATIONS) {
ACPIInternalUpdateFlags(
&(childExtension->Flags),
DEV_PROP_INVALID_RELATIONS,
TRUE
);
IoInvalidateDeviceRelations(
childExtension->PhysicalDeviceObject,
BusRelations
);
continue;
}
//
// Recurse
//
ACPITableUnloadInvalidateRelations( childExtension );
} // for ( ... )
//
// Done
//
return STATUS_SUCCESS;
}