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.
7417 lines
184 KiB
7417 lines
184 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
buildsrc.c
|
|
|
|
Abstract:
|
|
|
|
This module is used to 'build' associations and new device objects.
|
|
It contains functionality that was within detect.c but split to make
|
|
the files more readable
|
|
|
|
Someone asked me to describe how the building of a device extension
|
|
works
|
|
|
|
|
|
PhaseAdrOrHid
|
|
|
|
|
------------------------------------------------
|
|
| |
|
|
PhaseAdr PhaseUid
|
|
| |--------------------------|
|
|
|-------------------| PhaseHid
|
|
|--------------------------|
|
|
| PhaseCid
|
|
|--------------------------|
|
|
|
|
|
PhaseSta
|
|
|
|
|
PhaseEjd
|
|
|
|
|
---------------------------|
|
|
| PhaseCrs
|
|
---------------------------|
|
|
PhasePrw
|
|
|
|
|
PhasePr0
|
|
|
|
|
PhasePr1
|
|
|
|
|
PhasePr2
|
|
|
|
|
----------------------
|
|
| |
|
|
| PhasePsc
|
|
|--------------------|
|
|
|
|
|
PhasePsc+1
|
|
|
|
Author:
|
|
|
|
Stephane Plante (splante)
|
|
|
|
Environment:
|
|
|
|
NT Kernel Model Driver only
|
|
|
|
Revision History:
|
|
|
|
July 7, 1997 - Complete Rewrite
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,ACPIBuildFlushQueue)
|
|
#endif
|
|
|
|
//
|
|
// This is the variable that indicates wether or not the BUILD DPC is running
|
|
//
|
|
BOOLEAN AcpiBuildDpcRunning;
|
|
|
|
//
|
|
// This is set to true if we have done the fixed button enumeration
|
|
//
|
|
BOOLEAN AcpiBuildFixedButtonEnumerated;
|
|
|
|
//
|
|
// This is the variable that indicates wether or not the BUILD DPC has
|
|
// completed real work
|
|
//
|
|
BOOLEAN AcpiBuildWorkDone;
|
|
|
|
//
|
|
// This is the lock used to the entry queue
|
|
//
|
|
KSPIN_LOCK AcpiBuildQueueLock;
|
|
|
|
//
|
|
// This is the list that requests are queued onto. You must be holding the
|
|
// QueueLock to access this list
|
|
//
|
|
LIST_ENTRY AcpiBuildQueueList;
|
|
|
|
//
|
|
// This is the list for Devices
|
|
//
|
|
LIST_ENTRY AcpiBuildDeviceList;
|
|
|
|
//
|
|
// This is the list for Operation Regions
|
|
//
|
|
LIST_ENTRY AcpiBuildOperationRegionList;
|
|
|
|
//
|
|
// This is the list for Power Resources
|
|
//
|
|
LIST_ENTRY AcpiBuildPowerResourceList;
|
|
|
|
//
|
|
// This is the list entry for the running Control Methods
|
|
//
|
|
LIST_ENTRY AcpiBuildRunMethodList;
|
|
|
|
//
|
|
//
|
|
// This is the list for Synchronization with external (to the DPC anyways)
|
|
// threads. Items in this list are blocked on an event.
|
|
//
|
|
LIST_ENTRY AcpiBuildSynchronizationList;
|
|
|
|
//
|
|
// This is the list for Thermal Zones
|
|
//
|
|
LIST_ENTRY AcpiBuildThermalZoneList;
|
|
|
|
//
|
|
// This is what we use to queue up the DPC
|
|
//
|
|
KDPC AcpiBuildDpc;
|
|
|
|
//
|
|
// This is the list that we use to pre-allocate storage for requests
|
|
//
|
|
NPAGED_LOOKASIDE_LIST BuildRequestLookAsideList;
|
|
|
|
//
|
|
// This is the table used to map functions for the Device case. The indices
|
|
// are based on the WORK_DONE_xxx fields
|
|
//
|
|
PACPI_BUILD_FUNCTION AcpiBuildDeviceDispatch[] = {
|
|
ACPIBuildProcessGenericComplete, // WORK_DONE_COMPLETE
|
|
NULL, // WORK_DONE_PENDING
|
|
ACPIBuildProcessDeviceFailure, // WORK_DONE_FAILURE
|
|
ACPIBuildProcessDevicePhaseAdrOrHid, // WORK_DONE_STEP_ADR_OR_UID
|
|
ACPIBuildProcessDevicePhaseAdr, // WORK_DONE_STEP_ADR
|
|
ACPIBuildProcessDevicePhaseHid, // WORK_DONE_STEP_HID
|
|
ACPIBuildProcessDevicePhaseUid, // WORK_DONE_STEP_UID
|
|
ACPIBuildProcessDevicePhaseCid, // WORK_DONE_STEP_CID
|
|
ACPIBuildProcessDevicePhaseSta, // WORK_DONE_STEP_STA
|
|
ACPIBuildProcessDeviceGenericEvalStrict, // WORK_DONE_STEP_EJD
|
|
ACPIBuildProcessDevicePhaseEjd, // WORK_DONE_STEP_EJD + 1
|
|
ACPIBuildProcessDeviceGenericEvalStrict, // WORK_DONE_STEP_PRW
|
|
ACPIBuildProcessDevicePhasePrw, // WORK_DONE_STEP_PRW + 1
|
|
ACPIBuildProcessDeviceGenericEvalStrict, // WORK_DONE_STEP_PR0
|
|
ACPIBuildProcessDevicePhasePr0, // WORK_DONE_STEP_PR0 + 1
|
|
ACPIBuildProcessDeviceGenericEvalStrict, // WORK_DONE_STEP_PR1
|
|
ACPIBuildProcessDevicePhasePr1, // WORK_DONE_STEP_PR1 + 1
|
|
ACPIBuildProcessDeviceGenericEvalStrict, // WORK_DONE_STEP_PR2
|
|
ACPIBuildProcessDevicePhasePr2, // WORK_DONE_STEP_PR2 + 1
|
|
ACPIBuildProcessDeviceGenericEvalStrict, // WORK_DONE_STEP_CRS
|
|
ACPIBuildProcessDevicePhaseCrs, // WORK_DONE_STEP_CRS + 1
|
|
ACPIBuildProcessDeviceGenericEval, // WORK_DONE_STEP_PSC
|
|
ACPIBuildProcessDevicePhasePsc, // WORK_DONE_STEP_PSC + 1
|
|
};
|
|
|
|
//
|
|
// This is the table that is used to map the level of WorkDone with the
|
|
// object that we are currently looking for
|
|
//
|
|
ULONG AcpiBuildDevicePowerNameLookup[] = {
|
|
0, // WORK_DONE_COMPLETE
|
|
0, // WORK_DONE_PENDING
|
|
0, // WORK_DONE_FAILURE
|
|
0, // WORK_DONE_ADR_OR_HID
|
|
0, // WORK_DONE_ADR
|
|
0, // WORK_DONE_HID
|
|
0, // WORK_DONE_UID
|
|
0, // WORK_DONE_CID
|
|
0, // WORK_DONE_STA
|
|
PACKED_EJD, // WORK_DONE_EJD
|
|
0, // WORK_DONE_EJD + 1
|
|
PACKED_PRW, // WORK_DONE_PRW
|
|
0, // WORK_DONE_PRW + 1
|
|
PACKED_PR0, // WORK_DONE_PR0
|
|
0, // WORK_DONE_PR0 + 1
|
|
PACKED_PR1, // WORK_DONE_PR1
|
|
0, // WORK_DONE_PR1 + 1
|
|
PACKED_PR2, // WORK_DONE_PR2
|
|
0, // WORK_DONE_PR2 + 1
|
|
PACKED_CRS, // WORK_DONE_CRS
|
|
0, // WORK_DONE_CRS + 1
|
|
PACKED_PSC, // WORK_DONE_PSC
|
|
0, // WORK_DONE_PSC + 1
|
|
};
|
|
|
|
//
|
|
// We aren't using the Operation Region dispatch point yet
|
|
//
|
|
PACPI_BUILD_FUNCTION AcpiBuildOperationRegionDispatch[] = {
|
|
ACPIBuildProcessGenericComplete, // WORK_DONE_COMPLETE
|
|
NULL, // WORK_DONE_PENDING
|
|
NULL, // WORK_DONE_FAILURE
|
|
NULL // WORK_DONE_STEP_0
|
|
};
|
|
|
|
//
|
|
// This is the table used to map functions for the PowerResource case.
|
|
// The indices are based on the WORK_DONE_xxx fields
|
|
//
|
|
PACPI_BUILD_FUNCTION AcpiBuildPowerResourceDispatch[] = {
|
|
ACPIBuildProcessGenericComplete, // WORK_DONE_COMPLETE
|
|
NULL, // WORK_DONE_PENDING
|
|
ACPIBuildProcessPowerResourceFailure, // WORK_DONE_FAILURE
|
|
ACPIBuildProcessPowerResourcePhase0, // WORK_DONE_STEP_0
|
|
ACPIBuildProcessPowerResourcePhase1 // WORK_DONE_STEP_1
|
|
};
|
|
|
|
//
|
|
// This is the table used to map functions for the RunMethod case.
|
|
// The indices are based on the WORK_DONE_xxx fields
|
|
//
|
|
PACPI_BUILD_FUNCTION AcpiBuildRunMethodDispatch[] = {
|
|
ACPIBuildProcessGenericComplete, // WORK_DONE_COMPLETE,
|
|
NULL, // WORK_DONE_PENDING
|
|
NULL, // WORK_DONE_FAILURE
|
|
ACPIBuildProcessRunMethodPhaseCheckSta, // WORK_DONE_STEP_0
|
|
ACPIBuildProcessRunMethodPhaseCheckBridge, // WORK_DONE_STEP_1
|
|
ACPIBuildProcessRunMethodPhaseRunMethod, // WORK_DONE_STEP_2
|
|
ACPIBuildProcessRunMethodPhaseRecurse // WORK_DONE_STEP_3
|
|
};
|
|
|
|
//
|
|
// This is the table used to map functions for the ThermalZone case.
|
|
// The indices are based on the WORK_DONE_xxx fields
|
|
//
|
|
PACPI_BUILD_FUNCTION AcpiBuildThermalZoneDispatch[] = {
|
|
ACPIBuildProcessGenericComplete, // WORK_DONE_COMPLETE
|
|
NULL, // WORK_DONE_PENDING
|
|
NULL, // WORK_DONE_FAILURE
|
|
ACPIBuildProcessThermalZonePhase0 // WORK_DONE_STEP_0
|
|
};
|
|
|
|
VOID
|
|
ACPIBuildCompleteCommon(
|
|
IN PULONG OldWorkDone,
|
|
IN ULONG NewWorkDone
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Since the completion routines all have to do some bit of common work to
|
|
get the DPC firing again, this routine reduces the code duplication
|
|
|
|
Arguments:
|
|
|
|
OldWorkDone - Pointer to the old amount of work done
|
|
NewWorkDone - The new amount of work that has been completed
|
|
|
|
NOTENOTE: There is an implicit assumption that the current value of
|
|
WorkDone in the request is WORK_DONE_PENDING. If that is
|
|
not the case, we will fail to transition to the next stage,
|
|
which means that we will loop forever.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Update the state of the request
|
|
//
|
|
InterlockedCompareExchange( OldWorkDone, NewWorkDone,WORK_DONE_PENDING);
|
|
|
|
//
|
|
// We need this lock to look at the following variables
|
|
//
|
|
KeAcquireSpinLock( &AcpiBuildQueueLock, &oldIrql );
|
|
|
|
//
|
|
// No matter what, work was done
|
|
//
|
|
AcpiBuildWorkDone = TRUE;
|
|
|
|
//
|
|
// Is the DPC already running?
|
|
//
|
|
if (!AcpiBuildDpcRunning) {
|
|
|
|
//
|
|
// Better make sure that it does then
|
|
//
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLock( &AcpiBuildQueueLock, oldIrql );
|
|
|
|
}
|
|
|
|
VOID EXPORT
|
|
ACPIBuildCompleteGeneric(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA ObjectData,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a generic completion handler. If the interperter has successfully
|
|
execute the method, it completes the request to the next desired WORK_DONE,
|
|
otherwise, it fails the request
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - Points to the control that was run
|
|
Status - Result of the method
|
|
ObjectData - Information about the result
|
|
Context - PACPI_BUILD_REQUEST
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest = (PACPI_BUILD_REQUEST) Context;
|
|
ULONG nextWorkDone = buildRequest->NextWorkDone;
|
|
|
|
//
|
|
// Device what state we should transition to next
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Remember why we failed, but do not mark the request as being failed
|
|
//
|
|
buildRequest->Status = Status;
|
|
|
|
}
|
|
|
|
//
|
|
// Note: we don't have a race condition here because only one
|
|
// routine can be processing a request at any given time. Thus it
|
|
// is safe for us to specify a new next phase
|
|
//
|
|
buildRequest->NextWorkDone = WORK_DONE_FAILURE;
|
|
|
|
//
|
|
// Transition to the next stage
|
|
//
|
|
ACPIBuildCompleteCommon(
|
|
&(buildRequest->WorkDone),
|
|
nextWorkDone
|
|
);
|
|
|
|
}
|
|
|
|
VOID EXPORT
|
|
ACPIBuildCompleteMustSucceed(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA ObjectData,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a generic completion handler. If the interperter has successfully
|
|
execute the method, it completes the request to the next desired WORK_DONE,
|
|
otherwise, it fails the request
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - Points to the control that was run
|
|
Status - Result of the method
|
|
ObjectData - Information about the result
|
|
Context - PACPI_BUILD_REQUEST
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest = (PACPI_BUILD_REQUEST) Context;
|
|
ULONG nextWorkDone = buildRequest->NextWorkDone;
|
|
|
|
//
|
|
// Device what state we should transition to next
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Remember why we failed, and mark the request as being failed
|
|
//
|
|
buildRequest->Status = Status;
|
|
|
|
//
|
|
// Death
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_FAILED_MUST_SUCCEED_METHOD,
|
|
(ULONG_PTR) AcpiObject,
|
|
Status,
|
|
(AcpiObject ? AcpiObject->dwNameSeg : 0)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Note: we don't have a race condition here because only one
|
|
// routine can be processing a request at any given time. Thus it
|
|
// is safe for us to specify a new next phase
|
|
//
|
|
buildRequest->NextWorkDone = WORK_DONE_FAILURE;
|
|
|
|
//
|
|
// Transition to the next stage
|
|
//
|
|
ACPIBuildCompleteCommon(
|
|
&(buildRequest->WorkDone),
|
|
nextWorkDone
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
ACPIBuildDeviceDpc(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DpcContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is where all of the device extension related work is done.
|
|
It looks at queued requests and processes them as appropriate.
|
|
|
|
READ THIS:
|
|
|
|
The design of this DPC is such that it goes out and tries to find
|
|
work to do. Only if it finds no work does it stop. For this reason,
|
|
one *cannot* use a 'break' statement within the main 'do - while()'
|
|
loop. A continue must be use. Additionally, the code cannot make
|
|
assumptions that at a certain point, that any of the lists are assumed
|
|
to be empty. The code *must* use the IsListEmpty() macro to ensure
|
|
that lists that should be empty are in fact empty.
|
|
|
|
Arguments:
|
|
|
|
None used
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
UNREFERENCED_PARAMETER( Dpc );
|
|
UNREFERENCED_PARAMETER( DpcContext );
|
|
UNREFERENCED_PARAMETER( SystemArgument1 );
|
|
UNREFERENCED_PARAMETER( SystemArgument2 );
|
|
|
|
//
|
|
// First step is to acquire the DPC Lock, and check to see if another
|
|
// DPC is already running
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
if (AcpiBuildDpcRunning) {
|
|
|
|
//
|
|
// The DPC is already running, so we need to exit now
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that the DPC is now running
|
|
//
|
|
AcpiBuildDpcRunning = TRUE;
|
|
|
|
//
|
|
// We must try to do *some* work
|
|
//
|
|
do {
|
|
|
|
//
|
|
// Assume that we won't do any work
|
|
//
|
|
AcpiBuildWorkDone = FALSE;
|
|
|
|
//
|
|
// If there are items in the Request Queue, then move them to the
|
|
// proper list
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildQueueList ) ) {
|
|
|
|
//
|
|
// Sort the list
|
|
//
|
|
ACPIBuildProcessQueueList();
|
|
|
|
}
|
|
|
|
//
|
|
// We can release the spin lock now
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// If there are items in the Run Method list, then process the
|
|
// list
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildRunMethodList ) ) {
|
|
|
|
//
|
|
// We actually care what this call returns. The reason we do
|
|
// is that we want all of the control methods to be run before
|
|
// we do any of the following steps
|
|
//
|
|
status = ACPIBuildProcessGenericList(
|
|
&AcpiBuildRunMethodList,
|
|
AcpiBuildRunMethodDispatch
|
|
);
|
|
|
|
//
|
|
// We must own the spin lock before we do the following...
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// If we got back STATUS_PENDING, that means that there's
|
|
// a method queued in the interpreter somewhere. This will
|
|
// cause the DPC to (eventually) become scheduled again.
|
|
// That means that we don't have to do anything special to
|
|
// handle it.
|
|
//
|
|
if (status == STATUS_PENDING) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// The case that is special is where we are do get STATUS_SUCCESS
|
|
// back. This indicates that we've drained the list. The little
|
|
// fly in the ointment is that we might have scheduled other
|
|
// run requests, but those are stuck in the BuildQueue list. So
|
|
// what we need to do here is check to see if the BuildQueue list
|
|
// is non-empty and if it is, set the AcpiBuildWorkDone to TRUE
|
|
// so that we iterage again (and move the elements to the proper
|
|
// list).
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildQueueList) ) {
|
|
|
|
AcpiBuildWorkDone = TRUE;
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// If we've reached this point, then the Run list must be complete
|
|
// and there must be no items in the BuildQueue list. This means
|
|
// that's its safe to drop the lock and continue
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
}
|
|
|
|
//
|
|
// If there are items in the Operation Region list, then process
|
|
// the list
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildOperationRegionList ) ) {
|
|
|
|
//
|
|
// Since we don't block on this list --- ie: we can create
|
|
// operation regions at any time that we want, we don't care what
|
|
// this function returns.
|
|
//
|
|
status = ACPIBuildProcessGenericList(
|
|
&AcpiBuildOperationRegionList,
|
|
AcpiBuildOperationRegionDispatch
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// If there are items in the Power Resource list, then process
|
|
// the list
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildPowerResourceList ) ) {
|
|
|
|
//
|
|
// We actually care what this call returns. The reason we do
|
|
// is that we want all of the power resources to be built before
|
|
// we do any of the following steps
|
|
//
|
|
status = ACPIBuildProcessGenericList(
|
|
&AcpiBuildPowerResourceList,
|
|
AcpiBuildPowerResourceDispatch
|
|
);
|
|
if (status == STATUS_PENDING) {
|
|
|
|
//
|
|
// We must own the spinlock before we continue
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there are items in Device list, then process the list
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildDeviceList ) ) {
|
|
|
|
//
|
|
// Since we don't block on this list --- ie we can create
|
|
// devices at any time that we want, we don't care what this
|
|
// function returns.
|
|
//
|
|
status = ACPIBuildProcessGenericList(
|
|
&AcpiBuildDeviceList,
|
|
AcpiBuildDeviceDispatch
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// If there are items in the Thermal list, then process the list
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildThermalZoneList ) ) {
|
|
|
|
//
|
|
// Since we don't block on this list --- ie we can create
|
|
// thermal zones at any time that we want, we don't care what this
|
|
// function returns.
|
|
//
|
|
status = ACPIBuildProcessGenericList(
|
|
&AcpiBuildThermalZoneList,
|
|
AcpiBuildThermalZoneDispatch
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// If we have emptied out all the lists, then we can issue the
|
|
// synchronization requests
|
|
//
|
|
if (IsListEmpty( &AcpiBuildDeviceList ) &&
|
|
IsListEmpty( &AcpiBuildOperationRegionList) &&
|
|
IsListEmpty( &AcpiBuildPowerResourceList) &&
|
|
IsListEmpty( &AcpiBuildRunMethodList) &&
|
|
IsListEmpty( &AcpiBuildThermalZoneList ) ) {
|
|
|
|
//
|
|
// Check to see if we have any devices in the Delayed queue for
|
|
// the Power DPC. Note that we have to own the power lock for
|
|
// this, so claim it now
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerQueueLock );
|
|
if (!IsListEmpty( &AcpiPowerDelayedQueueList) ) {
|
|
|
|
//
|
|
// Move the contents of the list over
|
|
//
|
|
ACPIInternalMoveList(
|
|
&AcpiPowerDelayedQueueList,
|
|
&AcpiPowerQueueList
|
|
);
|
|
|
|
//
|
|
// Schedule the DPC, if necessary
|
|
//
|
|
if (!AcpiPowerDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiPowerDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerQueueLock );
|
|
|
|
}
|
|
|
|
//
|
|
// This is our chance to look at the synchronization list and
|
|
// see if some of the events have occured
|
|
//
|
|
if (!IsListEmpty( &AcpiBuildSynchronizationList) ) {
|
|
|
|
//
|
|
// Since we don't block on this list --- ie we can notify the
|
|
// system that the lists are empty at any time that we want,
|
|
// we don't care about what this function returns
|
|
//
|
|
status = ACPIBuildProcessSynchronizationList(
|
|
&AcpiBuildSynchronizationList
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// We need the lock again, since we are about to check to see if
|
|
// we have completed some work
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
} while ( AcpiBuildWorkDone );
|
|
|
|
//
|
|
// The DPC is no longer running
|
|
//
|
|
AcpiBuildDpcRunning = FALSE;
|
|
|
|
//
|
|
// We no longer need the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildDeviceExtension(
|
|
IN PNSOBJ CurrentObject OPTIONAL,
|
|
IN PDEVICE_EXTENSION ParentDeviceExtension OPTIONAL,
|
|
OUT PDEVICE_EXTENSION *ReturnExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just creates the bare frameworks for an ACPI device extension.
|
|
No control methods can be run at this point in time.
|
|
|
|
N.B: This routine is called with AcpiDeviceTreeLock being held by the
|
|
caller. So this routine executes at DISPATCH_LEVEL
|
|
|
|
Arguments:
|
|
|
|
|
|
CurrentObject - The object that we will link into the tree
|
|
ParentDeviceExtension - Where to link the deviceExtension into
|
|
ReturnExtension - Where we store a pointer to what we just created
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PACPI_POWER_INFO powerInfo;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
//
|
|
// Sanity checks
|
|
//
|
|
if (ParentDeviceExtension) {
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
//
|
|
// We must be under the tree lock.
|
|
//
|
|
//ASSERT_SPINLOCK_HELD(&AcpiDeviceTreeLock) ;
|
|
}
|
|
|
|
//
|
|
// Make sure that the current device doesn't already have a device extension
|
|
// This shouldn't really happen --- if it did, the interpreter called us
|
|
// twice, which is a bug on its part.
|
|
//
|
|
if ( CurrentObject != NULL &&
|
|
(PDEVICE_EXTENSION) CurrentObject->Context != NULL) {
|
|
|
|
//
|
|
// We have a value --- in theory, it should point to a DeviceExtension
|
|
//
|
|
deviceExtension = (PDEVICE_EXTENSION) CurrentObject->Context;
|
|
|
|
//
|
|
// It might not be safe to deref this
|
|
//
|
|
ASSERT( deviceExtension->ParentExtension == ParentDeviceExtension);
|
|
if (deviceExtension->ParentExtension == ParentDeviceExtension) {
|
|
|
|
//
|
|
// This again requires some thought: processing the same node
|
|
// again insn't a failure
|
|
//
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// This is probably a bad place to be since we deref'ed something
|
|
// that may or may not exist
|
|
//
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
|
|
}
|
|
|
|
//
|
|
// Create a new extension for the object
|
|
//
|
|
deviceExtension = ExAllocateFromNPagedLookasideList(
|
|
&DeviceExtensionLookAsideList
|
|
);
|
|
if (deviceExtension == NULL) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
"ACPIBuildDeviceExtension: NS %08lx - No Memory for "
|
|
"extension\n",
|
|
CurrentObject
|
|
) );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// Lets begin with a clean slate
|
|
//
|
|
RtlZeroMemory( deviceExtension, sizeof(DEVICE_EXTENSION) );
|
|
|
|
//
|
|
// Initialize the reference count mechanism. We only have a NS object
|
|
// so the value should be 1
|
|
//
|
|
deviceExtension->ReferenceCount++ ;
|
|
|
|
//
|
|
// The initial outstanding IRP count will be set to one. Only during a
|
|
// remove IRP will this drop to zero, and then it will immediately pop
|
|
// back up to one.
|
|
//
|
|
deviceExtension->OutstandingIrpCount++;
|
|
|
|
//
|
|
// Initialize the link fields
|
|
//
|
|
deviceExtension->AcpiObject = CurrentObject;
|
|
|
|
//
|
|
// Initialize the data fields
|
|
//
|
|
deviceExtension->Signature = ACPI_SIGNATURE;
|
|
deviceExtension->Flags = DEV_TYPE_NOT_FOUND | DEV_TYPE_NOT_PRESENT;
|
|
deviceExtension->DispatchTable = NULL;
|
|
deviceExtension->DeviceState = Stopped;
|
|
*ReturnExtension = deviceExtension;
|
|
|
|
//
|
|
// Setup some of the power information values
|
|
//
|
|
powerInfo = &(deviceExtension->PowerInfo);
|
|
powerInfo->DevicePowerMatrix[PowerSystemUnspecified] =
|
|
PowerDeviceUnspecified;
|
|
powerInfo->DevicePowerMatrix[PowerSystemWorking] = PowerDeviceD0;
|
|
powerInfo->DevicePowerMatrix[PowerSystemSleeping1] = PowerDeviceD0;
|
|
powerInfo->DevicePowerMatrix[PowerSystemSleeping2] = PowerDeviceD0;
|
|
powerInfo->DevicePowerMatrix[PowerSystemSleeping3] = PowerDeviceD0;
|
|
powerInfo->DevicePowerMatrix[PowerSystemHibernate] = PowerDeviceD3;
|
|
powerInfo->DevicePowerMatrix[PowerSystemShutdown] = PowerDeviceD3;
|
|
powerInfo->SystemWakeLevel = PowerSystemUnspecified;
|
|
powerInfo->DeviceWakeLevel = PowerDeviceUnspecified;
|
|
|
|
//
|
|
// Initialize the list entries
|
|
//
|
|
InitializeListHead( &(deviceExtension->ChildDeviceList) );
|
|
InitializeListHead( &(deviceExtension->EjectDeviceHead) );
|
|
InitializeListHead( &(deviceExtension->EjectDeviceList) );
|
|
InitializeListHead( &(powerInfo->WakeSupportList) );
|
|
InitializeListHead( &(powerInfo->PowerRequestListEntry) );
|
|
|
|
//
|
|
// Make sure that the deviceExtension has pointers to its parent
|
|
// extension. Note, that this should cause the ref count on the
|
|
// parent to increase
|
|
//
|
|
deviceExtension->ParentExtension = ParentDeviceExtension;
|
|
|
|
if (ParentDeviceExtension) {
|
|
|
|
InterlockedIncrement( &(ParentDeviceExtension->ReferenceCount) );
|
|
|
|
//
|
|
// Add the deviceExtension into the deviceExtension tree
|
|
//
|
|
InsertTailList(
|
|
&(ParentDeviceExtension->ChildDeviceList),
|
|
&(deviceExtension->SiblingDeviceList)
|
|
);
|
|
}
|
|
|
|
//
|
|
// And make sure that the Name Space Object points to the extension
|
|
//
|
|
if (CurrentObject != NULL ) {
|
|
|
|
CurrentObject->Context = deviceExtension;
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildDevicePowerNodes(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PNSOBJ ResultObject,
|
|
IN POBJDATA ResultData,
|
|
IN DEVICE_POWER_STATE DeviceState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds the Device Power Nodes for a Device, using the
|
|
given result data as a template
|
|
|
|
N.B. This routine is always called at DPC_LEVEL
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device to build power nodes for
|
|
ResultObject - The object that was used to get the data
|
|
ResultData - Information about the power nodes
|
|
DeviceState - The power state the information is for. Note that we
|
|
use PowerDeviceUnspecified for the Wake capabilities
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PACPI_DEVICE_POWER_NODE deviceNode;
|
|
PACPI_DEVICE_POWER_NODE deviceNodePool;
|
|
PNSOBJ packageObject = NULL;
|
|
POBJDATA currentData;
|
|
ULONG count;
|
|
ULONG index = 0;
|
|
ULONG i;
|
|
|
|
//
|
|
// The number of nodes to build is based on what is in the package
|
|
//
|
|
count = ((PACKAGEOBJ *) ResultData->pbDataBuff)->dwcElements;
|
|
if (DeviceState == PowerDeviceUnspecified) {
|
|
|
|
//
|
|
// If this node doesn't have the bear minimum of entries then
|
|
// we should just crash
|
|
//
|
|
if (count < 2) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_PRW_PACKAGE_TOO_SMALL,
|
|
(ULONG_PTR) DeviceExtension,
|
|
(ULONG_PTR) ResultObject,
|
|
count
|
|
);
|
|
goto ACPIBuildDevicePowerNodesExit;
|
|
|
|
}
|
|
|
|
//
|
|
// The first two elements in the _PRW are taken up by other things
|
|
//
|
|
count -= 2;
|
|
|
|
//
|
|
// Remember to bias the count by 2
|
|
//
|
|
index = 2;
|
|
|
|
}
|
|
|
|
//
|
|
// Never allocate zero bytes of memory
|
|
//
|
|
if (count == 0) {
|
|
|
|
goto ACPIBuildDevicePowerNodesExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate a block of memory to hold the device nodes
|
|
//
|
|
deviceNode = deviceNodePool = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
count * sizeof(ACPI_DEVICE_POWER_NODE),
|
|
ACPI_POWER_POOLTAG
|
|
);
|
|
if (deviceNode == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// We need a spinlock for the following
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// Remember the device power nodes for this Dx state
|
|
//
|
|
DeviceExtension->PowerInfo.PowerNode[DeviceState] = deviceNode;
|
|
|
|
//
|
|
// Process all the nodes listed
|
|
//
|
|
for (i = 0; i < count; i++, index++) {
|
|
|
|
//
|
|
// Initialize the current device node
|
|
//
|
|
RtlZeroMemory( deviceNode, sizeof(ACPI_DEVICE_POWER_NODE) );
|
|
|
|
//
|
|
// Grab the current object data
|
|
//
|
|
currentData =
|
|
&( ( (PACKAGEOBJ *) ResultData->pbDataBuff)->adata[index]);
|
|
|
|
//
|
|
// Remember that we don't have the package object yet
|
|
//
|
|
packageObject = NULL;
|
|
|
|
//
|
|
// Turn this into a name space object
|
|
//
|
|
status = AMLIGetNameSpaceObject(
|
|
currentData->pbDataBuff,
|
|
ResultObject,
|
|
&packageObject,
|
|
0
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
DeviceExtension,
|
|
"ACPIBuildDevicePowerNodes: %s Status = %08lx\n",
|
|
currentData->pbDataBuff,
|
|
status
|
|
) );
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_PRX_CANNOT_FIND_OBJECT,
|
|
(ULONG_PTR) DeviceExtension,
|
|
(ULONG_PTR) ResultObject,
|
|
(ULONG_PTR) currentData->pbDataBuff
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that the associated power object is not null
|
|
//
|
|
if (packageObject == NULL ||
|
|
NSGETOBJTYPE(packageObject) != OBJTYPE_POWERRES) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
DeviceExtension,
|
|
"ACPIBuildDevicePowerNodes: %s references bad power object.\n",
|
|
currentData->pbDataBuff
|
|
) );
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_POWERRES,
|
|
(ULONG_PTR) DeviceExtension,
|
|
(ULONG_PTR) ResultObject,
|
|
(ULONG_PTR) currentData->pbDataBuff
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Find the associated power object.
|
|
//
|
|
deviceNode->PowerNode = (PACPI_POWER_DEVICE_NODE)
|
|
packageObject->Context;
|
|
|
|
//
|
|
// Determine the support system level, and other static values
|
|
//
|
|
deviceNode->SystemState = deviceNode->PowerNode->SystemLevel;
|
|
deviceNode->DeviceExtension = DeviceExtension;
|
|
deviceNode->AssociatedDeviceState = DeviceState;
|
|
if (DeviceState == PowerDeviceUnspecified) {
|
|
|
|
deviceNode->WakePowerResource = TRUE;
|
|
|
|
}
|
|
if (DeviceState == PowerDeviceD0 &&
|
|
DeviceExtension->Flags & DEV_CAP_NO_OVERRIDE) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceNode->PowerNode->Flags),
|
|
(DEVICE_NODE_ALWAYS_ON | DEVICE_NODE_OVERRIDE_ON),
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Add the device to the list that the power node maintains
|
|
//
|
|
InsertTailList(
|
|
&(deviceNode->PowerNode->DevicePowerListHead),
|
|
&(deviceNode->DevicePowerListEntry)
|
|
);
|
|
|
|
//
|
|
// If this is not the last node, then make sure to keep a pointer
|
|
// to the next node
|
|
//
|
|
if (i < count - 1) {
|
|
|
|
deviceNode->Next = (deviceNode + 1);
|
|
|
|
} else {
|
|
|
|
deviceNode->Next = NULL;
|
|
}
|
|
|
|
//
|
|
// Point to the next node in the array of device nodes
|
|
//
|
|
deviceNode++;
|
|
|
|
}
|
|
|
|
//
|
|
// Done with lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
|
|
|
ACPIBuildDevicePowerNodesExit:
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildDeviceRequest(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PACPI_BUILD_CALLBACK CallBack,
|
|
IN PVOID CallBackContext,
|
|
IN BOOLEAN RunDPC
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a device extension is ready to be filled in.
|
|
This routine creates a request which is enqueued. When the DPC is fired,
|
|
the request will be processed
|
|
|
|
Note: AcpiDeviceTreeLock must be held to call this function
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device which wants to be filled in
|
|
CallBack - The function to call when done
|
|
CallBackContext - The argument to pass to that function
|
|
RunDPC - Should we enqueue the DPC immediately (if it is not
|
|
running?)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Allocate a buildRequest structure
|
|
//
|
|
buildRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (buildRequest == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// If the current reference is 0, that means that someone else beat
|
|
// use to the device extension that that we *CANNOT* touch it
|
|
//
|
|
if (DeviceExtension->ReferenceCount == 0) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
buildRequest
|
|
);
|
|
return STATUS_DEVICE_REMOVED;
|
|
|
|
} else {
|
|
|
|
InterlockedIncrement( &(DeviceExtension->ReferenceCount) );
|
|
|
|
}
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( buildRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
buildRequest->Signature = ACPI_SIGNATURE;
|
|
buildRequest->TargetListEntry = &AcpiBuildDeviceList;
|
|
buildRequest->WorkDone = WORK_DONE_STEP_0;
|
|
buildRequest->Status = STATUS_SUCCESS;
|
|
buildRequest->CallBack = CallBack;
|
|
buildRequest->CallBackContext = CallBackContext;
|
|
buildRequest->BuildContext = DeviceExtension;
|
|
buildRequest->Flags = BUILD_REQUEST_VALID_TARGET |
|
|
BUILD_REQUEST_DEVICE;
|
|
|
|
//
|
|
// At this point, we need the spinlock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Add this to the list
|
|
//
|
|
InsertTailList(
|
|
&AcpiBuildQueueList,
|
|
&(buildRequest->ListEntry)
|
|
);
|
|
|
|
//
|
|
// Do we need to queue up the DPC?
|
|
//
|
|
if (RunDPC && !AcpiBuildDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildFilter(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PDEVICE_OBJECT PdoObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a device object for the given device extension and
|
|
attaches to the specified PDO
|
|
|
|
Arguments:
|
|
|
|
DriverObject - This is used for IoCreateDevice
|
|
DeviceExtension - The extension to create a PDO for
|
|
PdoObject - The stack to attach the PDO to
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
|
|
KIRQL oldIrql;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT newDeviceObject = NULL;
|
|
PDEVICE_OBJECT targetDeviceObject = NULL;
|
|
|
|
//
|
|
// First step is to create a device object
|
|
//
|
|
status = IoCreateDevice(
|
|
DriverObject,
|
|
0,
|
|
NULL,
|
|
FILE_DEVICE_ACPI,
|
|
FILE_AUTOGENERATED_DEVICE_NAME,
|
|
FALSE,
|
|
&newDeviceObject
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// Attach the device to the PDO
|
|
//
|
|
targetDeviceObject = IoAttachDeviceToDeviceStack(
|
|
newDeviceObject,
|
|
PdoObject
|
|
);
|
|
if (targetDeviceObject == NULL) {
|
|
|
|
//
|
|
// Bad. We could not attach to a PDO. So we must fail this
|
|
//
|
|
IoDeleteDevice( newDeviceObject );
|
|
|
|
//
|
|
// This is as good as it gets
|
|
//
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, we have succeeded in creating everything we need
|
|
// so lets update the device extension.
|
|
//
|
|
// First, we need the lock
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
|
|
//
|
|
// Now, update the links
|
|
//
|
|
newDeviceObject->DeviceExtension = DeviceExtension;
|
|
DeviceExtension->DeviceObject = newDeviceObject;
|
|
DeviceExtension->PhysicalDeviceObject = PdoObject;
|
|
DeviceExtension->TargetDeviceObject = targetDeviceObject;
|
|
|
|
//
|
|
// Setup initial reference counts.
|
|
//
|
|
InterlockedIncrement( &(DeviceExtension->ReferenceCount) );
|
|
|
|
//
|
|
// Update the flags for the extension
|
|
//
|
|
ACPIInternalUpdateFlags( &(DeviceExtension->Flags), DEV_MASK_TYPE, TRUE );
|
|
ACPIInternalUpdateFlags( &(DeviceExtension->Flags), DEV_TYPE_FILTER, FALSE );
|
|
DeviceExtension->PreviousState = DeviceExtension->DeviceState;
|
|
DeviceExtension->DeviceState = Stopped;
|
|
DeviceExtension->DispatchTable = &AcpiFilterIrpDispatch;
|
|
|
|
//
|
|
// Propagate the Pdo's requirements
|
|
//
|
|
newDeviceObject->StackSize = targetDeviceObject->StackSize + 1;
|
|
newDeviceObject->AlignmentRequirement =
|
|
targetDeviceObject->AlignmentRequirement;
|
|
|
|
if (targetDeviceObject->Flags & DO_POWER_PAGABLE) {
|
|
|
|
newDeviceObject->Flags |= DO_POWER_PAGABLE;
|
|
|
|
}
|
|
|
|
if (targetDeviceObject->Flags & DO_DIRECT_IO) {
|
|
|
|
newDeviceObject->Flags |= DO_DIRECT_IO;
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the device lock
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
//
|
|
// We are done initializing the device object
|
|
//
|
|
newDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildFixedButtonExtension(
|
|
IN PDEVICE_EXTENSION ParentExtension,
|
|
OUT PDEVICE_EXTENSION *ResultExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a device extension for the fixed button if one is
|
|
detected
|
|
|
|
N.B. This function is called with ACPIDeviceTreeLock being owned
|
|
|
|
Arguments:
|
|
|
|
ParentExtension - Which child are we?
|
|
ResultExtension - Where to store the created extension
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG buttonCaps;
|
|
ULONG fixedEnables;
|
|
|
|
//
|
|
// Have we already done this?
|
|
//
|
|
if (AcpiBuildFixedButtonEnumerated) {
|
|
|
|
//
|
|
// Make sure not to return anything
|
|
//
|
|
*ResultExtension = NULL;
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
AcpiBuildFixedButtonEnumerated = TRUE;
|
|
|
|
//
|
|
// Lets look at the Fixed enables
|
|
//
|
|
fixedEnables = ACPIEnableQueryFixedEnables();
|
|
buttonCaps = 0;
|
|
if (fixedEnables & PM1_PWRBTN_EN) {
|
|
|
|
buttonCaps |= SYS_BUTTON_POWER;
|
|
|
|
}
|
|
if (fixedEnables & PM1_SLEEPBTN_EN) {
|
|
|
|
buttonCaps |= SYS_BUTTON_SLEEP;
|
|
|
|
}
|
|
|
|
//
|
|
// If we have no caps, then do nothing
|
|
//
|
|
if (!buttonCaps) {
|
|
|
|
*ResultExtension = NULL;
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// By default, the button can wake the computer
|
|
//
|
|
buttonCaps |= SYS_BUTTON_WAKE;
|
|
|
|
//
|
|
// Build the device extension
|
|
//
|
|
status = ACPIBuildDeviceExtension(
|
|
NULL,
|
|
ParentExtension,
|
|
ResultExtension
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Make sure not to return anything
|
|
//
|
|
*ResultExtension = NULL;
|
|
return status;
|
|
|
|
}
|
|
deviceExtension = *ResultExtension;
|
|
|
|
//
|
|
// Set the flags for the device
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_NO_OBJECT | DEV_CAP_RAW |
|
|
DEV_MASK_INTERNAL_DEVICE | DEV_CAP_BUTTON),
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Initialize the button specific extension
|
|
//
|
|
KeInitializeSpinLock( &deviceExtension->Button.SpinLock);
|
|
deviceExtension->Button.Capabilities = buttonCaps;
|
|
|
|
//
|
|
// Create the HID for the device
|
|
//
|
|
deviceExtension->DeviceID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
strlen(ACPIFixedButtonId) + 1,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (deviceExtension->DeviceID == NULL) {
|
|
|
|
//
|
|
// Mark the device as having failed init
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_FAILED_INIT,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
*ResultExtension = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
RtlCopyMemory(
|
|
deviceExtension->DeviceID,
|
|
ACPIFixedButtonId,
|
|
strlen(ACPIFixedButtonId) + 1
|
|
);
|
|
|
|
//
|
|
// Remember that we now have an _HID
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID),
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildFlushQueue(
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will block until the Build Queues have been flushed
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device which wants to flush the queue
|
|
|
|
Return Value:
|
|
|
|
NSTATUS
|
|
|
|
--*/
|
|
{
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Initialize the event that we will wait on
|
|
//
|
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|
|
|
//
|
|
// Now, push a request onto the stack such that when the build
|
|
// list has been flushed, we unblock this thread
|
|
//
|
|
status = ACPIBuildSynchronizationRequest(
|
|
DeviceExtension,
|
|
ACPIBuildNotifyEvent,
|
|
&event,
|
|
&AcpiBuildDeviceList,
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// Block until its done
|
|
//
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(
|
|
&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Let the world know
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildMissingChildren(
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk the ACPI namespace children of this device extension and create
|
|
device extension for any of the missing devices.
|
|
|
|
N.B. This function is called with the device tree locked...
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Extension to walk
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PNSOBJ nsObject;
|
|
ULONG objType;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
if (DeviceExtension->Flags & DEV_PROP_NO_OBJECT) {
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Walk all of children of this object
|
|
//
|
|
for (nsObject = NSGETFIRSTCHILD(DeviceExtension->AcpiObject);
|
|
nsObject != NULL;
|
|
nsObject = NSGETNEXTSIBLING(nsObject)) {
|
|
|
|
//
|
|
// Does the namespace object already have a context object? If so,
|
|
// then the object likely already has an extension...
|
|
//
|
|
if (nsObject->Context != NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, we possible don't have a device extension
|
|
// (depending on the object type) so we need to simulate an Object
|
|
// Creation call, similar to what OSNotifyCreate() does
|
|
//
|
|
objType = nsObject->ObjData.dwDataType;
|
|
switch (objType) {
|
|
case OBJTYPE_DEVICE:
|
|
status = OSNotifyCreateDevice(
|
|
nsObject,
|
|
DEV_PROP_REBUILD_CHILDREN
|
|
);
|
|
break;
|
|
case OBJTYPE_OPREGION:
|
|
status = OSNotifyCreateOperationRegion(
|
|
nsObject
|
|
);
|
|
break;
|
|
case OBJTYPE_PROCESSOR:
|
|
status = OSNotifyCreateProcessor(
|
|
nsObject,
|
|
DEV_PROP_REBUILD_CHILDREN
|
|
);
|
|
break;
|
|
case OBJTYPE_THERMALZONE:
|
|
status = OSNotifyCreateThermalZone(
|
|
nsObject,
|
|
DEV_PROP_REBUILD_CHILDREN
|
|
);
|
|
break;
|
|
default:
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
"ACPIBuildMissingChildren: Error %x when building %x\n",
|
|
status,
|
|
nsObject
|
|
) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildMissingEjectionRelations(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the elements from the AcpiUnresolvedEjectList and tries
|
|
to resolve them
|
|
|
|
N.B. This function can only be called IRQL < DISPATCH_LEVEL
|
|
|
|
Argument
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
LIST_ENTRY tempList;
|
|
LONG oldReferenceCount;
|
|
NTSTATUS status;
|
|
OBJDATA objData;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_EXTENSION ejectorExtension;
|
|
PNSOBJ ejdObject;
|
|
PNSOBJ ejdTarget;
|
|
|
|
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Initialize the list
|
|
//
|
|
InitializeListHead( &tempList);
|
|
|
|
//
|
|
// We need the device tree lock to manipulate the UnresolvedEject list
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
|
|
//
|
|
// Check to see if there is work to do...
|
|
//
|
|
if (IsListEmpty( &AcpiUnresolvedEjectList ) ) {
|
|
|
|
//
|
|
// No work todo
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Move the list so that we can release the lock...
|
|
//
|
|
ACPIInternalMoveList( &AcpiUnresolvedEjectList, &tempList );
|
|
|
|
//
|
|
// As long as we haven't drained the list, look at each element...
|
|
//
|
|
while (!IsListEmpty( &tempList ) ) {
|
|
|
|
//
|
|
// Get the corresponding device extension and remove the entry
|
|
// from the list
|
|
//
|
|
deviceExtension = (PDEVICE_EXTENSION) CONTAINING_RECORD(
|
|
tempList.Flink,
|
|
DEVICE_EXTENSION,
|
|
EjectDeviceList
|
|
);
|
|
RemoveEntryList( tempList.Flink );
|
|
|
|
//
|
|
// See if the _EJD object exists --- it really should otherwise we
|
|
// wouldn't be here..
|
|
//
|
|
ejdObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_EJD
|
|
);
|
|
if (!ejdObject) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Grab a reference to the object since we will be dropping the
|
|
// DeviceTreeLock.
|
|
//
|
|
InterlockedIncrement( &(deviceExtension->ReferenceCount) );
|
|
|
|
//
|
|
// Done with the lock for now...
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
//
|
|
// Evaluate it... Note that we are not holding the lock at this point,
|
|
// so its safe to call the blocking semantic version of the API
|
|
//
|
|
status = AMLIEvalNameSpaceObject(
|
|
ejdObject,
|
|
&objData,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Hold the device tree lock while we look for a match
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
|
|
//
|
|
// Decrement the reference count...
|
|
//
|
|
oldReferenceCount = InterlockedDecrement( &(deviceExtension->ReferenceCount) );
|
|
if (oldReferenceCount == 0) {
|
|
|
|
//
|
|
// Free the extension...
|
|
//
|
|
ACPIInitDeleteDeviceExtension( deviceExtension );
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Now we can check to see if the call succeeded
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Be more forgiving and add the entry back to the unresolved list
|
|
//
|
|
InsertTailList(
|
|
&AcpiUnresolvedEjectList,
|
|
&(deviceExtension->EjectDeviceList)
|
|
);
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// However, we must get back a string from the BIOS...
|
|
//
|
|
if (objData.dwDataType != OBJTYPE_STRDATA) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_STRING,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) ejdObject,
|
|
objData.dwDataType
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// See what this object points to
|
|
//
|
|
ejdTarget = NULL;
|
|
status = AMLIGetNameSpaceObject(
|
|
objData.pbDataBuff,
|
|
NULL,
|
|
&ejdTarget,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Free the objData now
|
|
//
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
AMLIFreeDataBuffs( &objData, 1 );
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(status) || ejdTarget == NULL || ejdTarget->Context == NULL) {
|
|
|
|
//
|
|
// No, match. Be forgiving and add this entry back to the
|
|
// unresolved extension...
|
|
//
|
|
InsertTailList(
|
|
&AcpiUnresolvedEjectList,
|
|
&(deviceExtension->EjectDeviceList)
|
|
);
|
|
|
|
} else {
|
|
|
|
ejectorExtension = (PDEVICE_EXTENSION) ejdTarget->Context;
|
|
InsertTailList(
|
|
&(ejectorExtension->EjectDeviceHead),
|
|
&(deviceExtension->EjectDeviceList)
|
|
);
|
|
if (!(ejectorExtension->Flags & DEV_TYPE_NOT_FOUND)) {
|
|
|
|
IoInvalidateDeviceRelations(
|
|
ejectorExtension->PhysicalDeviceObject,
|
|
EjectionRelations
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the spinlock
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
ACPIBuildNotifyEvent(
|
|
IN PVOID BuildContext,
|
|
IN PVOID Context,
|
|
IN NTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when one of the queues that we are attempting
|
|
to synchronize on has gotten empty. The point of this routine is to
|
|
set an event so that we can resume processing in the proper thread.
|
|
|
|
Arguments:
|
|
|
|
BuildContext - Aka the Device Extension
|
|
Context - Aka the Event to set
|
|
Status - The result of the operation
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PKEVENT event = (PKEVENT) Context;
|
|
|
|
UNREFERENCED_PARAMETER( BuildContext );
|
|
UNREFERENCED_PARAMETER( Status );
|
|
|
|
//
|
|
// Set the event
|
|
//
|
|
KeSetEvent( event, IO_NO_INCREMENT, FALSE );
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildPdo(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PDEVICE_OBJECT ParentPdoObject,
|
|
IN BOOLEAN CreateAsFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a device object for the given device extension.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - This is used for IoCreateDevice
|
|
DeviceExtension - The extension to create a PDO for
|
|
ParentPdoObject - Used to get reference required for filter
|
|
CreateAsFilter - If we should create as a PDO-Filter
|
|
|
|
Return Status:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT filterDeviceObject = NULL;
|
|
PDEVICE_OBJECT newDeviceObject = NULL;
|
|
|
|
//
|
|
// First step is to create a device object
|
|
//
|
|
status = IoCreateDevice(
|
|
DriverObject,
|
|
0,
|
|
NULL,
|
|
FILE_DEVICE_ACPI,
|
|
FILE_AUTOGENERATED_DEVICE_NAME,
|
|
FALSE,
|
|
&newDeviceObject
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// Next step is device if we should create the extension as a filter
|
|
// or not
|
|
//
|
|
if (CreateAsFilter) {
|
|
|
|
if (!(DeviceExtension->Flags & DEV_CAP_NO_FILTER) ) {
|
|
|
|
filterDeviceObject = IoGetAttachedDeviceReference(
|
|
ParentPdoObject
|
|
);
|
|
|
|
//
|
|
// Did we fail to attach?
|
|
//
|
|
if (filterDeviceObject == NULL) {
|
|
|
|
//
|
|
// We failed --- we must clean up this device object
|
|
//
|
|
IoDeleteDevice( newDeviceObject );
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CreateAsFilter = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, we have succeeded in creating everything we need
|
|
// so lets update the device extension.
|
|
//
|
|
// First, we need the lock
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
|
|
//
|
|
// Now, update the links and the reference counts
|
|
//
|
|
newDeviceObject->DeviceExtension = DeviceExtension;
|
|
DeviceExtension->DeviceObject = newDeviceObject;
|
|
DeviceExtension->PhysicalDeviceObject = newDeviceObject;
|
|
InterlockedIncrement( &(DeviceExtension->ReferenceCount) );
|
|
|
|
//
|
|
// Update the flags for the extension
|
|
//
|
|
ACPIInternalUpdateFlags( &(DeviceExtension->Flags), DEV_MASK_TYPE, TRUE );
|
|
ACPIInternalUpdateFlags( &(DeviceExtension->Flags), DEV_TYPE_PDO, FALSE );
|
|
DeviceExtension->PreviousState = DeviceExtension->DeviceState;
|
|
DeviceExtension->DeviceState = Stopped;
|
|
|
|
//
|
|
// Set the Irp Dispatch point
|
|
//
|
|
DeviceExtension->DispatchTable = &AcpiPdoIrpDispatch;
|
|
|
|
//
|
|
// Did we have to create as a PDO-Filter
|
|
//
|
|
if (CreateAsFilter) {
|
|
|
|
//
|
|
// Update the target field
|
|
//
|
|
DeviceExtension->TargetDeviceObject = filterDeviceObject;
|
|
|
|
//
|
|
// Update the flags to indicate that this a filter
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(DeviceExtension->Flags),
|
|
DEV_TYPE_FILTER,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Update the Irp Dispatch point
|
|
//
|
|
DeviceExtension->DispatchTable = &AcpiBusFilterIrpDispatch;
|
|
|
|
//
|
|
// Update the deviceObject information...
|
|
//
|
|
newDeviceObject->StackSize = filterDeviceObject->StackSize + 1;
|
|
newDeviceObject->AlignmentRequirement =
|
|
filterDeviceObject->AlignmentRequirement;
|
|
if (filterDeviceObject->Flags & DO_POWER_PAGABLE) {
|
|
|
|
newDeviceObject->Flags |= DO_POWER_PAGABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// A further refinition of the PDO is to see if it one of the 'special'
|
|
// internal devices
|
|
//
|
|
if (DeviceExtension->Flags & DEV_CAP_PROCESSOR) {
|
|
|
|
DeviceExtension->DispatchTable = &AcpiProcessorIrpDispatch;
|
|
|
|
} else if (DeviceExtension->Flags & DEV_PROP_HID) {
|
|
|
|
ULONG i;
|
|
PUCHAR ptr;
|
|
|
|
ASSERT( DeviceExtension->DeviceID );
|
|
|
|
for (i = 0; AcpiInternalDeviceTable[i].PnPId; i++) {
|
|
|
|
ptr = strstr(
|
|
DeviceExtension->DeviceID,
|
|
AcpiInternalDeviceTable[i].PnPId
|
|
);
|
|
if (ptr) {
|
|
|
|
DeviceExtension->DispatchTable =
|
|
AcpiInternalDeviceTable[i].DispatchTable;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Do some more specialized handling here
|
|
//
|
|
if (DeviceExtension->Flags & DEV_CAP_BUTTON &&
|
|
DeviceExtension->Flags & DEV_PROP_NO_OBJECT) {
|
|
|
|
//
|
|
// This means that this is the fixed button
|
|
//
|
|
FixedButtonDeviceObject = newDeviceObject;
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the device lock
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
//
|
|
// We are done initializing the device object
|
|
//
|
|
newDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
if (DeviceExtension->Flags & DEV_PROP_EXCLUSIVE) {
|
|
|
|
newDeviceObject->Flags |= DO_EXCLUSIVE;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildPowerResourceExtension(
|
|
IN PNSOBJ PowerResource,
|
|
OUT PACPI_POWER_DEVICE_NODE *ReturnNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a new power resource appears. This routine
|
|
builds the basic framework for the power resource. More data is filled
|
|
in latter
|
|
|
|
Note: this function is called with the AcpiDeviceTreeLock being held by
|
|
the caller
|
|
|
|
Arguments:
|
|
|
|
PowerResource - ACPI NameSpace Object that was added
|
|
ReturnNode - Where to store what we create
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_POWER_DEVICE_NODE powerNode;
|
|
PACPI_POWER_DEVICE_NODE tempNode;
|
|
PLIST_ENTRY listEntry;
|
|
PPOWERRESOBJ powerResourceObject;
|
|
|
|
//
|
|
// Allocate some memory for the power node
|
|
//
|
|
powerNode = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(ACPI_POWER_DEVICE_NODE),
|
|
ACPI_DEVICE_POOLTAG
|
|
);
|
|
if (powerNode == NULL) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
"ACPIBuildPowerResourceExtension: Could not allocate %08lx\n",
|
|
sizeof(ACPI_POWER_DEVICE_NODE)
|
|
) );
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// This will give us some useful data about the power resource
|
|
//
|
|
powerResourceObject = (PPOWERRESOBJ) (PowerResource->ObjData.pbDataBuff);
|
|
|
|
//
|
|
// Fill in the node. Note that the RtlZero explicitly clears all the flags.
|
|
// This is the desired behaviour
|
|
//
|
|
RtlZeroMemory( powerNode, sizeof(ACPI_POWER_DEVICE_NODE) );
|
|
powerNode->Flags = DEVICE_NODE_STA_UNKNOWN;
|
|
powerNode->PowerObject = PowerResource;
|
|
powerNode->ResourceOrder = powerResourceObject->bResOrder;
|
|
powerNode->WorkDone = WORK_DONE_STEP_0;
|
|
powerNode->SystemLevel = ACPIDeviceMapSystemState(
|
|
powerResourceObject->bSystemLevel
|
|
);
|
|
InitializeListHead( &powerNode->DevicePowerListHead );
|
|
*ReturnNode = powerNode;
|
|
|
|
//
|
|
// Make sure that the nsobj points to this entry.
|
|
//
|
|
PowerResource->Context = powerNode;
|
|
|
|
//
|
|
// We need to be holding the lock so that we add the node to the list
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// Grab the first element in the list and walk it
|
|
//
|
|
for (listEntry = AcpiPowerNodeList.Flink;
|
|
listEntry != &AcpiPowerNodeList;
|
|
listEntry = listEntry->Flink) {
|
|
|
|
//
|
|
// Look at the current node
|
|
//
|
|
tempNode = CONTAINING_RECORD(
|
|
listEntry,
|
|
ACPI_POWER_DEVICE_NODE,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// Should this node go *before* the current one?
|
|
//
|
|
if (tempNode->ResourceOrder >= powerNode->ResourceOrder) {
|
|
|
|
InsertTailList(
|
|
listEntry,
|
|
&(powerNode->ListEntry)
|
|
);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Did we loop all the way around?
|
|
//
|
|
if (listEntry == &AcpiPowerNodeList) {
|
|
|
|
//
|
|
// Yes? Oh well, we have to add the entry to the tail now
|
|
//
|
|
InsertTailList(
|
|
listEntry,
|
|
&(powerNode->ListEntry)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildPowerResourceRequest(
|
|
IN PACPI_POWER_DEVICE_NODE PowerNode,
|
|
IN PACPI_BUILD_CALLBACK CallBack,
|
|
IN PVOID CallBackContext,
|
|
IN BOOLEAN RunDPC
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a power node is ready to be filled in.
|
|
This routine creates a request which is enqueued. When the DPC is fired,
|
|
the request will be processed
|
|
|
|
Note: AcpiDeviceTreeLock must be held to call this function
|
|
|
|
Arguments:
|
|
|
|
PowerNode - The PowerNode that wants to be filled in
|
|
CallBack - The function to call when done
|
|
CallBackContext - The argument to pass to that function
|
|
RunDPC - Should we enqueue the DPC immediately (if it is not
|
|
running?)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Allocate a buildRequest structure
|
|
//
|
|
buildRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (buildRequest == NULL) {
|
|
|
|
//
|
|
// If there is a completion routine, call it
|
|
//
|
|
if (CallBack != NULL) {
|
|
|
|
(*CallBack)(
|
|
PowerNode,
|
|
CallBackContext,
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
);
|
|
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( buildRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
buildRequest->Signature = ACPI_SIGNATURE;
|
|
buildRequest->TargetListEntry = &AcpiBuildPowerResourceList;
|
|
buildRequest->WorkDone = WORK_DONE_STEP_0;
|
|
buildRequest->Status = STATUS_SUCCESS;
|
|
buildRequest->CallBack = CallBack;
|
|
buildRequest->CallBackContext = CallBackContext;
|
|
buildRequest->BuildContext = PowerNode;
|
|
buildRequest->Flags = BUILD_REQUEST_VALID_TARGET;
|
|
|
|
|
|
//
|
|
// At this point, we need the spinlock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Add this to the list
|
|
//
|
|
InsertTailList(
|
|
&AcpiBuildQueueList,
|
|
&(buildRequest->ListEntry)
|
|
);
|
|
|
|
//
|
|
// Do we need to queue up the DPC?
|
|
//
|
|
if (RunDPC && !AcpiBuildDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDeviceFailure(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handle the case where we failed to initialize the device
|
|
extension due to some error
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that failed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = BuildRequest->Status;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDeviceFailure: NextWorkDone = %lx Status = %08lx\n",
|
|
BuildRequest->NextWorkDone,
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Mark the node as having failed
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_FAILED_INIT,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Complete the request using the generic completion routine
|
|
//
|
|
status = ACPIBuildProcessGenericComplete( BuildRequest );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDeviceGenericEval(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is very generic. Since the remainder of the work involve
|
|
us executing a request then doing some specialized work on the result,
|
|
it is easy to share the common first part.
|
|
|
|
Path: PhaseX ---> PhaseX+1
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
ULONG objectName;
|
|
|
|
//
|
|
// Make sure that we clear the result
|
|
//
|
|
RtlZeroMemory( result, sizeof(OBJDATA) );
|
|
|
|
//
|
|
// Base everything on the current amount of workDone
|
|
//
|
|
objectName = AcpiBuildDevicePowerNameLookup[BuildRequest->CurrentWorkDone];
|
|
|
|
//
|
|
// Remember that the next work done is the CurrentWorkDone + 1
|
|
//
|
|
BuildRequest->NextWorkDone = BuildRequest->CurrentWorkDone + 1;
|
|
|
|
//
|
|
// Does this object exists?
|
|
//
|
|
BuildRequest->CurrentObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
objectName
|
|
);
|
|
if (BuildRequest->CurrentObject != NULL) {
|
|
|
|
//
|
|
// Yes, then call that function
|
|
//
|
|
status = AMLIAsyncEvalObject(
|
|
BuildRequest->CurrentObject,
|
|
result,
|
|
0,
|
|
NULL,
|
|
ACPIBuildCompleteGeneric,
|
|
BuildRequest
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// If we didn't get pending back, then call the method ourselves
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteGeneric(
|
|
BuildRequest->CurrentObject,
|
|
status,
|
|
result,
|
|
BuildRequest
|
|
);
|
|
|
|
}
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDeviceGenericEval: Phase%lx Status = %08lx\n",
|
|
BuildRequest->CurrentWorkDone - WORK_DONE_STEP_0,
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDeviceGenericEvalStrict(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is very generic. Since the remainder of the work involve
|
|
us executing a request then doing some specialized work on the result,
|
|
it is easy to share the common first part.
|
|
|
|
Path: PhaseX ---> PhaseX+1
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
ULONG objectName;
|
|
|
|
//
|
|
// Make sure that we clear the result
|
|
//
|
|
RtlZeroMemory( result, sizeof(OBJDATA) );
|
|
|
|
//
|
|
// Base everything on the current amount of workDone
|
|
//
|
|
objectName = AcpiBuildDevicePowerNameLookup[BuildRequest->CurrentWorkDone];
|
|
|
|
//
|
|
// Remember that the next work done is the CurrentWorkDone + 1
|
|
//
|
|
BuildRequest->NextWorkDone = BuildRequest->CurrentWorkDone + 1;
|
|
|
|
//
|
|
// Does this object exists?
|
|
//
|
|
BuildRequest->CurrentObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
objectName
|
|
);
|
|
if (BuildRequest->CurrentObject != NULL) {
|
|
|
|
//
|
|
// Yes, then call that function
|
|
//
|
|
status = AMLIAsyncEvalObject(
|
|
BuildRequest->CurrentObject,
|
|
result,
|
|
0,
|
|
NULL,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// What happened
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDeviceGenericEval: Phase%lx Status = %08lx\n",
|
|
BuildRequest->CurrentWorkDone - WORK_DONE_STEP_0,
|
|
status
|
|
) );
|
|
|
|
//
|
|
// If we didn't get pending back, then call the method ourselves
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
BuildRequest->CurrentObject,
|
|
status,
|
|
result,
|
|
BuildRequest
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseAdr(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the interpreter once it has evaluate the _ADR
|
|
method.
|
|
|
|
Path: PhaseAdr -> PhaseSta
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// If we got to this point, that means that the control method was
|
|
// successfull and so lets remember that we have an address
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_ADDRESS,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// The next phase is to run the _STA
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STA;
|
|
|
|
//
|
|
// Get the device status
|
|
//
|
|
status = ACPIGetDevicePresenceAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
(PVOID *) &(BuildRequest->Integer),
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// What happened?
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseAdr: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
} // ACPIBuildProcessDevicePhaseAdr
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseAdrOrHid(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called after all the children of the current device
|
|
have been created with the name space tree. This function is responsible
|
|
then for evaluating the 'safe' control methods to determine the name
|
|
of the extension, etc, etc
|
|
|
|
Path: PhaseAdrOrHid -> PhaseAdr
|
|
|-> PhaseUid
|
|
|-> PhaseHid
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
PNSOBJ nsObject = NULL;
|
|
POBJDATA resultData = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// We need to name this node, so lets determine if there is an _HID
|
|
// or an _ADR is present
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_HID
|
|
);
|
|
if (nsObject == NULL) {
|
|
|
|
//
|
|
// Otherwise, there had better be an _ADR present
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_ADR
|
|
);
|
|
if (nsObject == NULL) {
|
|
|
|
//
|
|
// At this point, we have an invalid name space object ---
|
|
// this should not happen
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_REQUIRED_METHOD_NOT_PRESENT,
|
|
(ULONG_PTR) deviceExtension,
|
|
PACKED_ADR,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Never get here
|
|
//
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we think there is an ADR, then the correct next stage is
|
|
// to post process the ADR
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_ADR;
|
|
|
|
//
|
|
// Remember which name space object we are evaluating
|
|
//
|
|
BuildRequest->CurrentObject = nsObject;
|
|
|
|
//
|
|
// Get the Address
|
|
//
|
|
status = ACPIGetAddressAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
(PVOID *) &(deviceExtension->Address),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Remember which name space object we are evaluating
|
|
//
|
|
BuildRequest->CurrentObject = nsObject;
|
|
|
|
//
|
|
// When we go down this path, we actually want to build the UID before
|
|
// the HID because that makes deciding wether to run the CID much easier
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_UID
|
|
);
|
|
if (nsObject != NULL) {
|
|
|
|
//
|
|
// If we think there is an UID, then the correct next stage is
|
|
// to postprocess the UID. The reason that
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_UID;
|
|
|
|
//
|
|
// Remember which name space object we are evaluating
|
|
//
|
|
BuildRequest->CurrentObject = nsObject;
|
|
|
|
//
|
|
// Get the Instance ID
|
|
//
|
|
status = ACPIGetInstanceIDAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
&(deviceExtension->InstanceID),
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We don't have UID, so lets process the HID
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_HID;
|
|
|
|
//
|
|
// Get the Device ID
|
|
//
|
|
status = ACPIGetDeviceIDAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
&(deviceExtension->DeviceID),
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
nsObject,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
} // ACPIBuildProcessDevicePhaseAdrOrUid
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseCid(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
This routine is called by the interpreter once it has evaluate the _CID
|
|
method. This routine then sets any flag that are appropriate
|
|
device
|
|
|
|
Path: PhaseCid -> PhaseSta
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
PUCHAR tempPtr = BuildRequest->String;
|
|
ULONG i;
|
|
|
|
//
|
|
// Walk the CID, trying to find the double NULL
|
|
//
|
|
for ( ;tempPtr != NULL && *tempPtr != '\0'; ) {
|
|
|
|
tempPtr += strlen(tempPtr);
|
|
if (*(tempPtr+1) == '\0') {
|
|
|
|
//
|
|
// Found the double null, so we can break
|
|
//
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the character to be a 'space'
|
|
//
|
|
*tempPtr = ' ';
|
|
|
|
}
|
|
tempPtr = BuildRequest->String;
|
|
|
|
//
|
|
// Set any special flags associated with this device id
|
|
//
|
|
for (i = 0; AcpiInternalDeviceFlagTable[i].PnPId != NULL; i++) {
|
|
|
|
if (strstr( tempPtr, AcpiInternalDeviceFlagTable[i].PnPId ) ) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
AcpiInternalDeviceFlagTable[i].Flags,
|
|
FALSE
|
|
);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the string
|
|
//
|
|
if (tempPtr != NULL) {
|
|
|
|
ExFreePool( tempPtr );
|
|
|
|
}
|
|
|
|
//
|
|
// The next stage is to run the _STA
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STA;
|
|
|
|
//
|
|
// Get the device status
|
|
//
|
|
status = ACPIGetDevicePresenceAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
(PVOID *) &(BuildRequest->Integer),
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// What happened?
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseCid: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseCrs(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the interpreter once it has evaluate the _CRS
|
|
method. This routine then determines if this is the kernel debugger
|
|
|
|
Path: PhaseCrs ---> PhasePrw
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// The next step is to run the _PRW
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_PRW;
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhaseCrsExit;
|
|
|
|
}
|
|
|
|
//
|
|
// We are expecting a package
|
|
//
|
|
if (result->dwDataType != OBJTYPE_BUFFDATA) {
|
|
|
|
//
|
|
// A bios must return a package to a PRW method
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_BUFFER,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
goto ACPIBuildProcessDevicePhaseCrsExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Update the bits to see if the serial port matches either the kernel debugger
|
|
// port or the kernel headless port.
|
|
//
|
|
ACPIMatchKernelPorts(
|
|
deviceExtension,
|
|
result
|
|
);
|
|
|
|
//
|
|
// Do not leave object lying around without having freed them first
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
ACPIBuildProcessDevicePhaseCrsExit:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseCrs: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseEjd(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we have run _EJD
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that has just been completed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
PDEVICE_EXTENSION ejectorExtension = NULL;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
PNSOBJ ejectObject = NULL;
|
|
|
|
//
|
|
// From here, decide if we have a serial port or not
|
|
//
|
|
if (!(deviceExtension->Flags & DEV_TYPE_NOT_PRESENT) &&
|
|
(deviceExtension->Flags & DEV_CAP_SERIAL) ) {
|
|
|
|
//
|
|
// The next step is to run the _CRS
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_CRS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The next step is to run the _PRW
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_PRW;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhaseEjdExit;
|
|
|
|
}
|
|
|
|
//
|
|
// No longer need the result
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
//
|
|
// Add the device extension into the unresolved eject tree
|
|
//
|
|
ExInterlockedInsertTailList(
|
|
&AcpiUnresolvedEjectList,
|
|
&(deviceExtension->EjectDeviceList),
|
|
&AcpiDeviceTreeLock
|
|
);
|
|
|
|
#if DBG
|
|
if (deviceExtension->DebugFlags & DEVDBG_EJECTOR_FOUND) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_WARNING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseEjd: Ejector already found\n"
|
|
) );
|
|
|
|
} else {
|
|
|
|
deviceExtension->DebugFlags |= DEVDBG_EJECTOR_FOUND;
|
|
|
|
}
|
|
#endif
|
|
|
|
ACPIBuildProcessDevicePhaseEjdExit:
|
|
|
|
//
|
|
// Check to see if we have a dock device
|
|
//
|
|
if (!ACPIDockIsDockDevice( deviceExtension->AcpiObject) ) {
|
|
|
|
//
|
|
// If it's not a dock, then don't bother...
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
goto ACPIBuildProcessDevicePhaseEjdExit2;
|
|
|
|
}
|
|
if (!AcpiInformation->Dockable) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_WARNING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseEjd: BIOS BUG - DOCK bit not set\n"
|
|
) );
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_CLAIMS_BOGUS_DOCK_SUPPORT,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
0
|
|
);
|
|
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Have we already handled this? --- This guy will grab the lock. So don't
|
|
// hold the DeviceTree Lock at this point
|
|
//
|
|
if (ACPIDockFindCorrespondingDock( deviceExtension ) ) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_CLAIMS_BOGUS_DOCK_SUPPORT,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
1
|
|
);
|
|
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We need the spinlock to touch the device tree
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiDeviceTreeLock );
|
|
|
|
//
|
|
// Build the device extension
|
|
//
|
|
status = ACPIBuildDockExtension(
|
|
deviceExtension->AcpiObject,
|
|
RootDeviceExtension
|
|
);
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiDeviceTreeLock );
|
|
|
|
ACPIBuildProcessDevicePhaseEjdExit2:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseEjd: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteGeneric(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseHid(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the interpreter once it has evaluate the _HID
|
|
method.
|
|
|
|
Path: PhaseHid -> PhaseCid
|
|
|-> PhaseSta
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN matchFound = FALSE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
PNSOBJ nsObject = NULL;
|
|
PUCHAR tempPtr = deviceExtension->DeviceID;
|
|
ULONG i;
|
|
|
|
//
|
|
// Set any special flags associated with this device id
|
|
//
|
|
for (i = 0; AcpiInternalDeviceFlagTable[i].PnPId != NULL; i++) {
|
|
|
|
if (strstr( tempPtr, AcpiInternalDeviceFlagTable[i].PnPId ) ) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
AcpiInternalDeviceFlagTable[i].Flags,
|
|
FALSE
|
|
);
|
|
matchFound = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we have an HID
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_HID,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Lets see if there is a _CID to run. Only run the _CID if there
|
|
// was no match found above
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_CID
|
|
);
|
|
if (nsObject != NULL && matchFound == FALSE) {
|
|
|
|
//
|
|
// The next phase is to post process the _CID
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_CID;
|
|
|
|
//
|
|
// Get the compatible ID
|
|
//
|
|
status = ACPIGetCompatibleIDAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
&(BuildRequest->String),
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The next step is to run the _STA
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STA;
|
|
|
|
//
|
|
// Get the device status
|
|
//
|
|
status = ACPIGetDevicePresenceAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
(PVOID *) &(BuildRequest->Integer),
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// What happened?
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseHid: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
nsObject,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
} // ACPIBuildProcessDevicePhaseHid
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhasePr0(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
This routine is called by the interpreter once it has evaluate the _PR0
|
|
method. This routine then determines the current power state of the
|
|
device
|
|
|
|
Path: PhasePr0 ---> PhasePr1
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// The next stage is PR1
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_PR1;
|
|
|
|
//
|
|
// Get the appropriate _PSx object to go with this object
|
|
//
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD0] =
|
|
ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_PS0
|
|
);
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhasePr0Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// We are expecting a package
|
|
//
|
|
if (result->dwDataType != OBJTYPE_PKGDATA) {
|
|
|
|
//
|
|
// A bios must return a package to a PRW method
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_PACKAGE,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
goto ACPIBuildProcessDevicePhasePr0Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Process the package
|
|
//
|
|
status = ACPIBuildDevicePowerNodes(
|
|
deviceExtension,
|
|
BuildRequest->CurrentObject,
|
|
result,
|
|
PowerDeviceD0
|
|
);
|
|
|
|
//
|
|
// Do not leave object lying around without having freed them first
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
ACPIBuildProcessDevicePhasePr0Exit:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhasePr0: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhasePr1(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
This routine is called by the interpreter once it has evaluate the _PR1
|
|
method. This routine then determines the current power state of the
|
|
device
|
|
|
|
Path: PhasePr1 ---> PhasePr2
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// The next stage is Phase16
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_PR2;
|
|
|
|
//
|
|
// Get the appropriate _PSx object to go with this object
|
|
//
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD1] =
|
|
ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_PS1
|
|
);
|
|
if (deviceExtension->PowerInfo.PowerObject[PowerDeviceD1] == NULL) {
|
|
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD1] =
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD0];
|
|
|
|
}
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhasePr1Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// We are expecting a package
|
|
//
|
|
if (result->dwDataType != OBJTYPE_PKGDATA) {
|
|
|
|
//
|
|
// A bios must return a package to a PRW method
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_PACKAGE,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
goto ACPIBuildProcessDevicePhasePr1Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Process the package
|
|
//
|
|
status = ACPIBuildDevicePowerNodes(
|
|
deviceExtension,
|
|
BuildRequest->CurrentObject,
|
|
result,
|
|
PowerDeviceD1
|
|
);
|
|
|
|
//
|
|
// Do not leave object lying around without having freed them first
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
ACPIBuildProcessDevicePhasePr1Exit:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhasePr1: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhasePr2(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
This routine is called by the interpreter once it has evaluate the _PR2
|
|
method. This routine then determines the current power state of the
|
|
device
|
|
|
|
Path: PhasePr2 ---> PhasePsc
|
|
|-> PhasePsc+1
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// Get the appropriate _PSx object to go with this object
|
|
//
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD2] =
|
|
ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_PS2
|
|
);
|
|
if (deviceExtension->PowerInfo.PowerObject[PowerDeviceD2] == NULL) {
|
|
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD2] =
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceD1];
|
|
|
|
}
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhasePr2Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// We are expecting a package
|
|
//
|
|
if (result->dwDataType != OBJTYPE_PKGDATA) {
|
|
|
|
//
|
|
// A bios must return a package to a PRW method
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_PACKAGE,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
goto ACPIBuildProcessDevicePhasePr2Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Process the package
|
|
//
|
|
status = ACPIBuildDevicePowerNodes(
|
|
deviceExtension,
|
|
BuildRequest->CurrentObject,
|
|
result,
|
|
PowerDeviceD2
|
|
);
|
|
|
|
//
|
|
// Do not leave object lying around without having freed them first
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
ACPIBuildProcessDevicePhasePr2Exit:
|
|
|
|
//
|
|
// If the device is not physically present, then we cannot run the _CRS and
|
|
// _PSC. If the device is not present, the we cannot run those two methods,
|
|
// but we can fake it..
|
|
//
|
|
if (deviceExtension->Flags & DEV_TYPE_NOT_PRESENT) {
|
|
|
|
BuildRequest->CurrentObject = NULL;
|
|
BuildRequest->NextWorkDone = (WORK_DONE_PSC + 1);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The next step is to run the _PSC
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_PSC;
|
|
|
|
}
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhasePr2: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhasePrw(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
This routine is called by the interpreter once it has evaluate the _PRW
|
|
method. This routine then determines the current power state of the
|
|
device
|
|
|
|
Path: PhasePRW ---> PhasePR0
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN ignorePrw = FALSE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
POBJDATA stateObject = NULL;
|
|
POBJDATA pinObject = NULL;
|
|
ULONG gpeRegister;
|
|
ULONG gpeMask;
|
|
|
|
//
|
|
// The next stage is Phase12
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_PR0;
|
|
|
|
//
|
|
// Get the appropriate _PSx object to go with this object
|
|
//
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceUnspecified] =
|
|
ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_PSW
|
|
);
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhasePrwExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Should we ignore the _PRW for this device?
|
|
//
|
|
if ( (AcpiOverrideAttributes & ACPI_OVERRIDE_OPTIONAL_WAKE) &&
|
|
!(deviceExtension->Flags & DEV_CAP_NO_DISABLE_WAKE) ) {
|
|
|
|
ignorePrw = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// We are expecting a package
|
|
//
|
|
if (result->dwDataType != OBJTYPE_PKGDATA) {
|
|
|
|
//
|
|
// A bios must return a package to a PRW method
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_PACKAGE,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Process the package
|
|
//
|
|
status = ACPIBuildDevicePowerNodes(
|
|
deviceExtension,
|
|
BuildRequest->CurrentObject,
|
|
result,
|
|
PowerDeviceUnspecified
|
|
);
|
|
|
|
//
|
|
// Hold the power lock for the following
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// Since this was a _PRW object, then we want to store a bit more information
|
|
// about the wake capabilities
|
|
//
|
|
|
|
//
|
|
// Set the GPE pin which will be used to wake the system
|
|
//
|
|
pinObject = &( ( (PACKAGEOBJ *) result->pbDataBuff)->adata[0]);
|
|
if (pinObject->dwDataType != OBJTYPE_INTDATA) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_PRW_PACKAGE_EXPECTED_INTEGER,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
pinObject->dwDataType
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Set the system wake level for the device
|
|
//
|
|
stateObject = &( ( (PACKAGEOBJ *) result->pbDataBuff)->adata[1]);
|
|
if (stateObject->dwDataType != OBJTYPE_INTDATA) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_PRW_PACKAGE_EXPECTED_INTEGER,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
stateObject->dwDataType
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Set these bits only if we support sleep
|
|
//
|
|
if (!ignorePrw) {
|
|
|
|
//
|
|
// First, store the pin that we use as the wakeup signal
|
|
//
|
|
deviceExtension->PowerInfo.WakeBit = (ULONG)pinObject->uipDataValue;
|
|
|
|
//
|
|
// Next, store the system state that we can wake up from
|
|
//
|
|
deviceExtension->PowerInfo.SystemWakeLevel = ACPIDeviceMapSystemState(
|
|
stateObject->uipDataValue
|
|
);
|
|
|
|
//
|
|
// Finally, lets set the Wake capabilities flag
|
|
//
|
|
ACPIInternalUpdateFlags( &(deviceExtension->Flags), DEV_CAP_WAKE, FALSE );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// Calculate the correct register and mask
|
|
//
|
|
gpeRegister = ( (UCHAR) (pinObject->uipDataValue) / 8);
|
|
gpeMask = 1 << ( (UCHAR) (pinObject->uipDataValue) % 8);
|
|
|
|
//
|
|
// We need access to the table lock for this
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &GpeTableLock );
|
|
|
|
//
|
|
// Does this vector have a GPE?
|
|
//
|
|
if ( (GpeEnable[gpeRegister] & gpeMask) ) {
|
|
|
|
//
|
|
// If we got here, and we aren't marked as DEV_CAP_NO_DISABLE, then we
|
|
// should turn off the GPE. The easiest way to do this is to make sure
|
|
// that the GpeWakeHandler[] vector is masked with the appropriate
|
|
// bit
|
|
//
|
|
if (!(deviceExtension->Flags & DEV_CAP_NO_DISABLE_WAKE) ) {
|
|
|
|
//
|
|
// It has a gpe mask, so remember that there is a wake handler
|
|
// for it. This should prevent us from arming the GPE without
|
|
// a request for it
|
|
//
|
|
if (!(GpeSpecialHandler[gpeRegister] & gpeMask) ) {
|
|
|
|
GpeWakeHandler[gpeRegister] |= gpeMask;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we got here, then we should remember that we can never
|
|
// consider this pin as *just* a wake handler
|
|
//
|
|
GpeSpecialHandler[gpeRegister] |= gpeMask;
|
|
|
|
//
|
|
// Make sure that the pin isn't set as a wake handler
|
|
//
|
|
if (GpeWakeHandler[gpeRegister] & gpeMask) {
|
|
|
|
//
|
|
// Clear the pin from the wake handler mask
|
|
//
|
|
GpeWakeHandler[gpeRegister] &= ~gpeMask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the table lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &GpeTableLock );
|
|
|
|
//
|
|
// Do not leave object lying around without having freed them first
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
//
|
|
// Finally, if there is a _PSW object, make sure that we run it to disable
|
|
// that capability --- this way we resume from a known state
|
|
//
|
|
if (deviceExtension->PowerInfo.PowerObject[PowerDeviceUnspecified]) {
|
|
|
|
OBJDATA argData;
|
|
|
|
//
|
|
// Setup the parameters
|
|
//
|
|
RtlZeroMemory( &argData, sizeof(OBJDATA) );
|
|
argData.dwDataType = OBJTYPE_INTDATA;
|
|
argData.uipDataValue = 0;
|
|
|
|
//
|
|
// Run the method. Note that we don't specify a callback because we
|
|
// don't actually care when it completes
|
|
//
|
|
AMLIAsyncEvalObject(
|
|
deviceExtension->PowerInfo.PowerObject[PowerDeviceUnspecified],
|
|
NULL,
|
|
1,
|
|
&argData,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
ACPIBuildProcessDevicePhasePrwExit:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhasePrw: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhasePsc(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the interpreter once it has evaluate the _PSC
|
|
method. This routine then determines the current power state of the
|
|
device
|
|
|
|
Path: PhasePsc ---> COMPLETE
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
DEVICE_POWER_STATE i;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PACPI_DEVICE_POWER_NODE deviceNode;
|
|
PACPI_POWER_INFO powerInfo;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
SYSTEM_POWER_STATE matrixIndex = PowerSystemSleeping1;
|
|
|
|
|
|
//
|
|
// The next stage is Complete
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
|
|
//
|
|
// We will use the power information structure a lot
|
|
//
|
|
powerInfo = &(deviceExtension->PowerInfo);
|
|
|
|
//
|
|
// Since we didn't get a change to look for the _PS3 object earlier,
|
|
// lets find it now. Note, that we cannot use the PS2 object if we don't
|
|
// find the PS3 object.
|
|
//
|
|
powerInfo->PowerObject[PowerDeviceD3] =
|
|
ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_PS3
|
|
);
|
|
|
|
//
|
|
// We must be holding a spinlock for the following
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// For each S state, walk PR0 to PR2 until you find a resource that
|
|
// cannot be ON in S state. The next lighter D state is then the lightest
|
|
// D state for the given S state.
|
|
//
|
|
for ( ; matrixIndex <= PowerSystemHibernate ; matrixIndex++ ) {
|
|
|
|
//
|
|
// Loop on all members of the PowerNode
|
|
//
|
|
for (i = PowerDeviceD0; i <= PowerDeviceD2; i++ ) {
|
|
|
|
//
|
|
// Are there any resources to look at?
|
|
//
|
|
deviceNode = powerInfo->PowerNode[i];
|
|
if (deviceNode == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
while (deviceNode != NULL &&
|
|
deviceNode->SystemState >= matrixIndex) {
|
|
|
|
deviceNode = deviceNode->Next;
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// If we have had a device node, but don't have now, that means
|
|
// that we found a D level that is compliant for this S-state
|
|
//
|
|
if (deviceNode == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildDeviceProcessPhasePsc: D%x <-> S%x\n",
|
|
(i - PowerDeviceD0),
|
|
matrixIndex - PowerSystemWorking
|
|
) );
|
|
|
|
//
|
|
// This device can be in Di state while in SmatrixIndex state
|
|
//
|
|
powerInfo->DevicePowerMatrix[matrixIndex] = i;
|
|
break;
|
|
|
|
}
|
|
|
|
} // for (i = PowerDeviceD0 ...
|
|
|
|
} // for ( ; matrixIndex ...
|
|
|
|
//
|
|
// Now that we have built the matrix, we can figure out what D-level the
|
|
// device can support wake with.
|
|
//
|
|
powerInfo->DeviceWakeLevel =
|
|
powerInfo->DevicePowerMatrix[powerInfo->SystemWakeLevel];
|
|
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// At this point, we have to decide what to do based on the result of
|
|
// the _PSC. The first step is assume that the device is in the D0 state
|
|
//
|
|
i = PowerDeviceD0;
|
|
|
|
//
|
|
// We will override the above if there is a bit that says that the device
|
|
// should start in the D3 state
|
|
//
|
|
if (deviceExtension->Flags & DEV_CAP_START_IN_D3) {
|
|
|
|
//
|
|
// Go directly to D3
|
|
//
|
|
i = PowerDeviceD3;
|
|
goto ACPIBuildProcessDevicePhasePscBuild;
|
|
|
|
}
|
|
|
|
//
|
|
// Did we have an object to run?
|
|
//
|
|
if (BuildRequest->CurrentObject == NULL) {
|
|
|
|
//
|
|
// No? Then there is no work for us to do here
|
|
//
|
|
goto ACPIBuildProcessDevicePhasePscBuild;
|
|
|
|
}
|
|
|
|
//
|
|
// If we didn't succeed the control method, assume that the device
|
|
// should be in the D0 state
|
|
//
|
|
if (!NT_SUCCESS(BuildRequest->Status)) {
|
|
|
|
goto ACPIBuildProcessDevicePhasePscBuild;
|
|
|
|
}
|
|
|
|
//
|
|
// Also, if we know that the device must always be in the D0 state, then
|
|
// we must ignore whatever the _PSC says
|
|
//
|
|
if (deviceExtension->Flags & DEV_CAP_ALWAYS_PS0) {
|
|
|
|
//
|
|
// Free the buffer
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
deviceExtension->PowerInfo.PowerState = i;
|
|
goto ACPIBuildProcessDevicePhasePscBuild;
|
|
|
|
}
|
|
|
|
//
|
|
// Did the request what we expected?
|
|
//
|
|
if (result->dwDataType != OBJTYPE_INTDATA) {
|
|
|
|
//
|
|
// A bios must return an integer for a _PSC
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_INTEGER,
|
|
(ULONG_PTR) deviceExtension,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
goto ACPIBuildProcessDevicePhasePscExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Turn the power state into something that we can understand
|
|
//
|
|
i = ACPIDeviceMapPowerState( result->uipDataValue );
|
|
|
|
//
|
|
// No longer need the buffer
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
ACPIBuildProcessDevicePhasePscBuild:
|
|
|
|
//
|
|
// Queue the request
|
|
//
|
|
status = ACPIDeviceInternalDelayedDeviceRequest(
|
|
deviceExtension,
|
|
i,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
ACPIBuildProcessDevicePhasePscExit:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhasePsc: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteGeneric(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseSta(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
This routine is called by the interpreter once it has evaluate the _STA
|
|
method. This routine then determines the current power state of the
|
|
device
|
|
|
|
Path: PhaseSta -> PhaseEjd
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// The next stage is to start running the _EJD
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_EJD;
|
|
|
|
//
|
|
// What happened
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseSta: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// See if the device conforms to the ACPI specification for HIDs and UIDs
|
|
// We do this at this point because we now know wether or not the device
|
|
// is present or not and that is an important test because the OEM is
|
|
// allowed to have 2 devices with the same HID/UID as long as both aren't
|
|
// present at the same time.
|
|
//
|
|
ACPIDetectDuplicateHID(
|
|
deviceExtension
|
|
);
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessDevicePhaseUid(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the interpreter once it has evaluate the _UID
|
|
method.
|
|
|
|
Path: PhaseUid --> PhaseHid
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we will try to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
PNSOBJ nsObject;
|
|
|
|
//
|
|
// Remember that we have an UID
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_UID,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Lets see if there is a _HID to run
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
PACKED_HID
|
|
);
|
|
if (nsObject != NULL) {
|
|
|
|
//
|
|
// The next phase is to post process the _HID
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_HID;
|
|
|
|
//
|
|
// Get the Device ID
|
|
//
|
|
status = ACPIGetDeviceIDAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
&(deviceExtension->DeviceID),
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not having an _HID is a fatal error
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_REQUIRED_METHOD_NOT_PRESENT,
|
|
(ULONG_PTR) deviceExtension,
|
|
PACKED_HID,
|
|
0
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// What happened
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessDevicePhaseUid: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
nsObject,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessGenericComplete(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we are done with the request
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that has just been completed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_CALLBACK callBack = BuildRequest->CallBack;
|
|
|
|
//
|
|
// Invoke the callback, if there is any
|
|
//
|
|
if (callBack != NULL) {
|
|
|
|
(*callBack)(
|
|
BuildRequest->BuildContext,
|
|
BuildRequest->CallBackContext,
|
|
BuildRequest->Status
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Do we have to release a reference on this request?
|
|
//
|
|
if (BuildRequest->Flags & BUILD_REQUEST_RELEASE_REFERENCE) {
|
|
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
LONG oldReferenceCount;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// We to have the device tree lock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiDeviceTreeLock );
|
|
|
|
//
|
|
// No longer need a reference to the device extension
|
|
//
|
|
InterlockedDecrement( &(deviceExtension->ReferenceCount) );
|
|
|
|
//
|
|
// Done with the device tree lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiDeviceTreeLock );
|
|
|
|
}
|
|
|
|
//
|
|
// We need the spinlock for this
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Remember that work was done --- this should be all that is required
|
|
// to have the currently running DPC process the next request
|
|
//
|
|
AcpiBuildWorkDone = TRUE;
|
|
|
|
//
|
|
// Remove the entry from the current list. We might not need to be
|
|
// hodling the lock to do this, but it doesn't pay to not do it while
|
|
// we can
|
|
//
|
|
RemoveEntryList( &(BuildRequest->ListEntry) );
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// We are done with the request memory
|
|
//
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessGenericList(
|
|
IN PLIST_ENTRY ListEntry,
|
|
IN PACPI_BUILD_FUNCTION *DispatchTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes all the build requests through the various
|
|
phases required to build a complete device extension
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN allWorkComplete = TRUE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PACPI_BUILD_FUNCTION buildFunction = NULL;
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
PLIST_ENTRY currentEntry = ListEntry->Flink;
|
|
PLIST_ENTRY tempEntry;
|
|
ULONG workDone;
|
|
|
|
while (currentEntry != ListEntry) {
|
|
|
|
//
|
|
// Turn into a build request
|
|
//
|
|
buildRequest = CONTAINING_RECORD(
|
|
currentEntry,
|
|
ACPI_BUILD_REQUEST,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// Set the temp pointer to the next element. The reason that this
|
|
// gets done is because once we call the dispatch function, the
|
|
// current request can be completed (and thus freed), so we need
|
|
// to remember whom the next person to process is.
|
|
//
|
|
tempEntry = currentEntry->Flink;
|
|
|
|
//
|
|
// Check to see if we have any work to do on the request
|
|
//
|
|
workDone = InterlockedCompareExchange(
|
|
&(buildRequest->WorkDone),
|
|
WORK_DONE_PENDING,
|
|
WORK_DONE_PENDING
|
|
);
|
|
|
|
//
|
|
// Look at the dispatch table to see if there is a function to
|
|
// call
|
|
//
|
|
buildFunction = DispatchTable[ workDone ];
|
|
if (buildFunction != NULL) {
|
|
|
|
//
|
|
// Just to help us along, if we are going to the failure
|
|
// path, then we should not update the Current Work Done field.
|
|
// This gives us an easy means of find which step failed
|
|
//
|
|
if (workDone != WORK_DONE_FAILURE) {
|
|
|
|
//
|
|
// Mark the node as being in the state 'workDone'
|
|
//
|
|
buildRequest->CurrentWorkDone = workDone;
|
|
|
|
}
|
|
|
|
//
|
|
// Mark the request as pending
|
|
//
|
|
workDone = InterlockedCompareExchange(
|
|
&(buildRequest->WorkDone),
|
|
WORK_DONE_PENDING,
|
|
workDone
|
|
);
|
|
|
|
//
|
|
// Call the function
|
|
//
|
|
status = (buildFunction)( buildRequest );
|
|
|
|
} else {
|
|
|
|
//
|
|
// The work is not all complete, and we should look at the
|
|
// next element
|
|
//
|
|
allWorkComplete = FALSE;
|
|
currentEntry = tempEntry;
|
|
|
|
//
|
|
// Loop
|
|
//
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// If we have completed the request, then we should look at the
|
|
// at the next request, otherwise, we need to look at the current
|
|
// request again
|
|
if ( workDone == WORK_DONE_COMPLETE || workDone == WORK_DONE_FAILURE) {
|
|
|
|
currentEntry = tempEntry;
|
|
|
|
}
|
|
|
|
} // while
|
|
|
|
//
|
|
// Have we completed all of our work?
|
|
//
|
|
return (allWorkComplete ? STATUS_SUCCESS : STATUS_PENDING );
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessorExtension(
|
|
IN PNSOBJ ProcessorObject,
|
|
IN PDEVICE_EXTENSION ParentExtension,
|
|
IN PDEVICE_EXTENSION *ResultExtension,
|
|
IN ULONG ProcessorIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Since we leverage ACPIBuildDeviceExtension for the core of the processor
|
|
extension, we don't have much to do here. However, we are responsible
|
|
for making sure that we do tasks that don't require calling the interpreter,
|
|
and an id unique to the processor
|
|
|
|
N.B. This function is called with AcpiDeviceTreeLock being held
|
|
|
|
Arguments:
|
|
|
|
ProcessorObject - The object which represents the processor
|
|
ParentExtension - Who our parent is
|
|
ResultExtension - Where to store the extension that we build
|
|
ProcessorIndex - Where do we find the processor in the ProcessorList
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
//
|
|
// If we did not get the correct ID out of the registry earlier, fail now.
|
|
//
|
|
if (AcpiProcessorString.Buffer == NULL) {
|
|
return(STATUS_OBJECT_NAME_NOT_FOUND);
|
|
}
|
|
|
|
//
|
|
// Build the extension
|
|
//
|
|
status = ACPIBuildDeviceExtension(
|
|
ProcessorObject,
|
|
ParentExtension,
|
|
ResultExtension
|
|
);
|
|
if (!NT_SUCCESS(status) || *ResultExtension == NULL) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// Grab a pointer to the device extension for easy usage
|
|
//
|
|
deviceExtension = *ResultExtension;
|
|
|
|
//
|
|
// Make sure to remember that this is in fact a processor
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_CAP_PROCESSOR | DEV_MASK_INTERNAL_DEVICE),
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Remember the the Index of this processor object in the processor
|
|
// array table
|
|
//
|
|
deviceExtension->Processor.ProcessorIndex = ProcessorIndex;
|
|
|
|
//
|
|
// Allocate memory for the HID
|
|
//
|
|
deviceExtension->DeviceID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
AcpiProcessorString.Length,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (deviceExtension->DeviceID == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
deviceExtension,
|
|
"ACPIBuildProcessorExtension: failed to allocate %08 bytes\n",
|
|
AcpiProcessorString.Length
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildProcessorExtensionExit;
|
|
|
|
}
|
|
RtlCopyMemory(
|
|
deviceExtension->DeviceID,
|
|
AcpiProcessorString.Buffer,
|
|
AcpiProcessorString.Length
|
|
);
|
|
|
|
//
|
|
// Allocate memory for the CID
|
|
//
|
|
deviceExtension->Processor.CompatibleID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
strlen(AcpiProcessorCompatId) + 1,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (deviceExtension->Processor.CompatibleID == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
deviceExtension,
|
|
"ACPIBuildProcessorExtension: failed to allocate %08 bytes\n",
|
|
strlen(AcpiProcessorCompatId) + 1
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildProcessorExtensionExit;
|
|
|
|
}
|
|
RtlCopyMemory(
|
|
deviceExtension->Processor.CompatibleID,
|
|
AcpiProcessorCompatId,
|
|
strlen(AcpiProcessorCompatId) + 1
|
|
);
|
|
|
|
//
|
|
// Allocate memory for the UID
|
|
//
|
|
deviceExtension->InstanceID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
3,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (deviceExtension->InstanceID == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
deviceExtension,
|
|
"ACPIBuildProcessorExtension: failed to allocate %08 bytes\n",
|
|
3
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildProcessorExtensionExit;
|
|
|
|
}
|
|
sprintf(deviceExtension->InstanceID,"%2d", ProcessorIndex );
|
|
|
|
//
|
|
// Set the flags for the work that we have just done
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID | DEV_PROP_FIXED_CID |
|
|
DEV_PROP_UID | DEV_PROP_FIXED_UID),
|
|
FALSE
|
|
);
|
|
|
|
ACPIBuildProcessorExtensionExit:
|
|
|
|
//
|
|
// Handle the case where we might have failed
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
deviceExtension,
|
|
"ACPIBuildProcessorExtension: = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
if (deviceExtension->InstanceID != NULL) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_UID | DEV_PROP_FIXED_UID),
|
|
TRUE
|
|
);
|
|
ExFreePool( deviceExtension->InstanceID );
|
|
deviceExtension->InstanceID = NULL;
|
|
|
|
}
|
|
|
|
if (deviceExtension->DeviceID != NULL) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID),
|
|
TRUE
|
|
);
|
|
ExFreePool( deviceExtension->DeviceID );
|
|
deviceExtension->DeviceID = NULL;
|
|
|
|
}
|
|
|
|
if (deviceExtension->Processor.CompatibleID != NULL) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_FIXED_CID),
|
|
TRUE
|
|
);
|
|
ExFreePool( deviceExtension->Processor.CompatibleID );
|
|
deviceExtension->Processor.CompatibleID = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we failed init
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_FAILED_INIT,
|
|
TRUE
|
|
);
|
|
|
|
} else {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessorExtension: = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessorRequest(
|
|
IN PDEVICE_EXTENSION ProcessorExtension,
|
|
IN PACPI_BUILD_CALLBACK CallBack,
|
|
IN PVOID CallBackContext,
|
|
IN BOOLEAN RunDPC
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a processor is ready to be filled in.
|
|
This routine creates a request which is enqueued. When the DPC is fired,
|
|
the request will be processed
|
|
|
|
Note: AcpiDeviceTreeLock must be held to call this function
|
|
|
|
Arguments:
|
|
|
|
ThermalExtension - The thermal zone to process
|
|
CallBack - The function to call when done
|
|
CallBackContext - The argument to pass to that function
|
|
RunDPC - Should we enqueue the DPC immediately (if it is not
|
|
running?)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
#if 0
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Allocate a buildRequest structure
|
|
//
|
|
buildRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (buildRequest == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// If the current reference is 0, that means that someone else beat
|
|
// use to the device extension that that we *CANNOT* touch it
|
|
//
|
|
if (ProcessorExtension->ReferenceCount == 0) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
buildRequest
|
|
);
|
|
return STATUS_DEVICE_REMOVED;
|
|
|
|
} else {
|
|
|
|
InterlockedIncrement( &(ProcessorExtension->ReferenceCount) );
|
|
|
|
}
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( buildRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
buildRequest->Signature = ACPI_SIGNATURE;
|
|
buildRequest->TargetListEntry = &AcpiBuildDeviceList;
|
|
buildRequest->WorkDone = WORK_DONE_STEP_0;
|
|
buildRequest->Status = STATUS_SUCCESS;
|
|
buildRequest->CallBack = CallBack;
|
|
buildRequest->CallBackContext = CallBackContext;
|
|
buildRequest->BuildContext = ProcessorExtension;
|
|
buildRequest->Flags = BUILD_REQUEST_VALID_TARGET |
|
|
BUILD_REQUEST_RELEASE_REFERENCE;
|
|
|
|
//
|
|
// At this point, we need the spinlock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Add this to the list
|
|
//
|
|
InsertTailList(
|
|
&AcpiBuildQueueList,
|
|
&(buildRequest->ListEntry)
|
|
);
|
|
|
|
//
|
|
// Do we need to queue up the DPC?
|
|
//
|
|
if (RunDPC && !AcpiBuildDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
#endif
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessPowerResourceFailure(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is run when we detect a failure in the Power Resource
|
|
initialization code path
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we have just failed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = BuildRequest->Status;
|
|
PACPI_POWER_DEVICE_NODE powerNode = (PACPI_POWER_DEVICE_NODE) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// Make sure that the node is marked as not being present and not having
|
|
// been initialized
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
|
ACPIInternalUpdateFlags(
|
|
&(powerNode->Flags),
|
|
(DEVICE_NODE_INITIALIZED | DEVICE_NODE_PRESENT),
|
|
TRUE
|
|
);
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// call the generic completion handler
|
|
//
|
|
status = ACPIBuildProcessGenericComplete( BuildRequest );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessPowerResourcePhase0(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the pointers to the _ON, _OFF, and _STA objects for
|
|
the associated power nodes. If these pointers cannot be found, the system
|
|
will bugcheck.
|
|
|
|
Once the pointers are found, the _STA method is evaluated
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_ACPI_FATAL;
|
|
PACPI_POWER_DEVICE_NODE powerNode = (PACPI_POWER_DEVICE_NODE) BuildRequest->BuildContext;
|
|
PNSOBJ nsObject;
|
|
POBJDATA resultData = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// The next state is Phase1
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STEP_1;
|
|
|
|
//
|
|
// Get the _OFF object
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
powerNode->PowerObject,
|
|
PACKED_OFF
|
|
);
|
|
if (nsObject == NULL) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_POWER_NODE_REQUIRED_METHOD_NOT_PRESENT,
|
|
(ULONG_PTR) powerNode->PowerObject,
|
|
PACKED_OFF,
|
|
0
|
|
);
|
|
goto ACPIBuildProcessPowerResourcePhase0Exit;
|
|
|
|
}
|
|
powerNode->PowerOffObject = nsObject;
|
|
|
|
//
|
|
// Get the _ON object
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
powerNode->PowerObject,
|
|
PACKED_ON
|
|
);
|
|
if (nsObject == NULL) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_POWER_NODE_REQUIRED_METHOD_NOT_PRESENT,
|
|
(ULONG_PTR) powerNode->PowerObject,
|
|
PACKED_ON,
|
|
0
|
|
);
|
|
goto ACPIBuildProcessPowerResourcePhase0Exit;
|
|
|
|
}
|
|
powerNode->PowerOnObject = nsObject;
|
|
|
|
//
|
|
// Get the _STA object
|
|
//
|
|
nsObject = ACPIAmliGetNamedChild(
|
|
powerNode->PowerObject,
|
|
PACKED_STA
|
|
);
|
|
if (nsObject == NULL) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_POWER_NODE_REQUIRED_METHOD_NOT_PRESENT,
|
|
(ULONG_PTR) powerNode->PowerObject,
|
|
PACKED_STA,
|
|
0
|
|
);
|
|
goto ACPIBuildProcessPowerResourcePhase0Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that our result data structure is 'clean'
|
|
//
|
|
RtlZeroMemory( resultData, sizeof(OBJDATA) );
|
|
|
|
//
|
|
// Remember the current object that we will evalute
|
|
//
|
|
BuildRequest->CurrentObject = nsObject;
|
|
|
|
//
|
|
// Evalute the _STA object
|
|
//
|
|
status = AMLIAsyncEvalObject(
|
|
nsObject,
|
|
resultData,
|
|
0,
|
|
NULL,
|
|
ACPIBuildCompleteGeneric,
|
|
BuildRequest
|
|
);
|
|
|
|
ACPIBuildProcessPowerResourcePhase0Exit:
|
|
|
|
//
|
|
// If we didn't get pending back, then call the method ourselves
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteGeneric(
|
|
nsObject,
|
|
status,
|
|
resultData,
|
|
BuildRequest
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessPowerResourcePhase1(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is run after we have finished the _STA method
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PACPI_POWER_DEVICE_NODE powerNode = (PACPI_POWER_DEVICE_NODE) BuildRequest->BuildContext;
|
|
POBJDATA result = &(BuildRequest->DeviceRequest.ResultData);
|
|
|
|
//
|
|
// The next stage is Complete
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
|
|
//
|
|
// Do we have an integer?
|
|
//
|
|
if (result->dwDataType != OBJTYPE_INTDATA) {
|
|
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_EXPECTED_INTEGER,
|
|
(ULONG_PTR) powerNode->PowerObject,
|
|
(ULONG_PTR) BuildRequest->CurrentObject,
|
|
result->dwDataType
|
|
);
|
|
status = STATUS_ACPI_FATAL;
|
|
goto ACPIBuildProcessPowerResourcePhase1Exit;
|
|
|
|
}
|
|
|
|
//
|
|
// We need the spinlock to do the following
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock );
|
|
|
|
//
|
|
// Marked the node as having been initialized
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(powerNode->Flags),
|
|
DEVICE_NODE_INITIALIZED,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Check the device status?
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(powerNode->Flags),
|
|
DEVICE_NODE_PRESENT,
|
|
(BOOLEAN) ((result->uipDataValue & STA_STATUS_PRESENT) ? FALSE : TRUE)
|
|
);
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock );
|
|
|
|
ACPIBuildProcessPowerResourcePhase1Exit:
|
|
|
|
//
|
|
// Do not leave objects lying around without having free'ed them first
|
|
//
|
|
AMLIFreeDataBuffs( result, 1 );
|
|
|
|
//
|
|
// We don't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have duplicate code
|
|
//
|
|
ACPIBuildCompleteGeneric(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessQueueList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks at all the items on the Queue list and places them
|
|
on the appropriate build list
|
|
|
|
N.B: This routine is called with AcpiBuildQueueLock being owned
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
PLIST_ENTRY currentEntry = AcpiBuildQueueList.Flink;
|
|
|
|
//
|
|
// Look at all the items in the list
|
|
//
|
|
while (currentEntry != &AcpiBuildQueueList) {
|
|
|
|
//
|
|
// Crack the data structure
|
|
//
|
|
buildRequest = CONTAINING_RECORD(
|
|
currentEntry,
|
|
ACPI_BUILD_REQUEST,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// Remove this entry from the Queue List
|
|
//
|
|
RemoveEntryList( currentEntry );
|
|
|
|
//
|
|
// Move this entry onto its new list
|
|
//
|
|
InsertTailList( buildRequest->TargetListEntry, currentEntry );
|
|
|
|
//
|
|
// We no longer need the TargetListEntry, so lets zero it to make
|
|
// sure that we don't run into problems
|
|
//
|
|
buildRequest->Flags &= ~BUILD_REQUEST_VALID_TARGET;
|
|
buildRequest->TargetListEntry = NULL;
|
|
|
|
//
|
|
// Look at the head of the list again
|
|
//
|
|
currentEntry = AcpiBuildQueueList.Flink;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessRunMethodPhaseCheckBridge(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the current object is present or not
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// Check the flags to see if we need to check the result of the device
|
|
// presence test
|
|
//
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_CHECK_STATUS) {
|
|
|
|
//
|
|
// Is the device present?
|
|
//
|
|
if ( (deviceExtension->Flags & DEV_TYPE_NOT_PRESENT) ) {
|
|
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
goto ACPIBuildProcessRunMethodPhaseCheckBridgeExit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// The next state is Phase2
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STEP_2;
|
|
|
|
//
|
|
// Do we have to check the device status?
|
|
//
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_STOP_AT_BRIDGES) {
|
|
|
|
//
|
|
// Get the device status
|
|
//
|
|
BuildRequest->Integer = 0;
|
|
status = IsPciBusAsync(
|
|
deviceExtension->AcpiObject,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
(BOOLEAN *) &(BuildRequest->Integer)
|
|
);
|
|
|
|
//
|
|
// What happened?
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessRunMethodPhaseCheckBridge: Status = %08lx\n",
|
|
status
|
|
) );
|
|
if (status == STATUS_PENDING) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ACPIBuildProcessRunMethodPhaseCheckBridgeExit:
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessRunMethodPhaseCheckSta(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the current object is present or not
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// The next state is Phase1
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STEP_1;
|
|
|
|
//
|
|
// Is this a device with a 'fake' PDO?
|
|
//
|
|
if (deviceExtension->Flags & DEV_PROP_NO_OBJECT) {
|
|
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
goto ACPIBuildProcessRunMethodPhaseCheckStaExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Do we have to check the device status?
|
|
//
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_CHECK_STATUS) {
|
|
|
|
//
|
|
// Get the device status
|
|
//
|
|
status = ACPIGetDevicePresenceAsync(
|
|
deviceExtension,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest,
|
|
(PVOID *) &(BuildRequest->Integer),
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// What happened?
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessRunMethodPhaseCheckSta: Status = %08lx\n",
|
|
status
|
|
) );
|
|
if (status == STATUS_PENDING) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ACPIBuildProcessRunMethodPhaseCheckStaExit:
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessRunMethodPhaseRecurse(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does the recursion
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
EXTENSIONLIST_ENUMDATA eled ;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION childExtension;
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
|
|
//
|
|
// We are done after this
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
|
|
//
|
|
// Do we recurse or not?
|
|
//
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_RECURSIVE) {
|
|
|
|
//
|
|
// Walk children
|
|
//
|
|
ACPIExtListSetupEnum(
|
|
&eled,
|
|
&(deviceExtension->ChildDeviceList),
|
|
&AcpiDeviceTreeLock,
|
|
SiblingDeviceList,
|
|
WALKSCHEME_HOLD_SPINLOCK
|
|
) ;
|
|
|
|
for(childExtension = ACPIExtListStartEnum(&eled);
|
|
ACPIExtListTestElement(&eled, (BOOLEAN) NT_SUCCESS(status));
|
|
childExtension = ACPIExtListEnumNext(&eled)) {
|
|
|
|
|
|
//
|
|
// Make a request to run the control method on this child
|
|
//
|
|
status = ACPIBuildRunMethodRequest(
|
|
childExtension,
|
|
NULL,
|
|
NULL,
|
|
BuildRequest->RunRequest.ControlMethodName,
|
|
BuildRequest->RunRequest.Flags,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// What happened
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessRunMethodPhaseRecurse: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Common code
|
|
//
|
|
ACPIBuildCompleteMustSucceed(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessRunMethodPhaseRunMethod(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if there is a control method to run
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
OBJDATA objData[2];
|
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
PNSOBJ nsObj = NULL;
|
|
POBJDATA args = NULL;
|
|
ULONGLONG originalFlags;
|
|
ULONG numArgs = 0;
|
|
|
|
//
|
|
// Check the flags to see if we need to check the result of the device
|
|
// presence test
|
|
//
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_STOP_AT_BRIDGES) {
|
|
|
|
//
|
|
// Is this a PCI-PCI bridge?
|
|
//
|
|
if (BuildRequest->Integer) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessRunMethodPhaseRunMethod: Is PCI-PCI bridge\n",
|
|
status
|
|
) );
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
goto ACPIBuildProcessRunMethodPhaseRunMethodExit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// From here, we need to go one more step
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_STEP_3;
|
|
|
|
//
|
|
// If there an object present?
|
|
//
|
|
nsObj = ACPIAmliGetNamedChild(
|
|
deviceExtension->AcpiObject,
|
|
BuildRequest->RunRequest.ControlMethodName
|
|
);
|
|
if (nsObj == NULL) {
|
|
|
|
//
|
|
// There is no method to run. Lets skip to the next stage then
|
|
//
|
|
goto ACPIBuildProcessRunMethodPhaseRunMethodExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Do we need to mark the node with the _INI flags?
|
|
//
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_MARK_INI) {
|
|
|
|
//
|
|
// Attempt to set the flag so that we don't run the method twice
|
|
//
|
|
originalFlags = ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_RAN_INI,
|
|
FALSE
|
|
);
|
|
if ( (originalFlags & DEV_PROP_RAN_INI) ) {
|
|
|
|
//
|
|
// If the flag was already set, then there is nothing for
|
|
// us to do here
|
|
//
|
|
goto ACPIBuildProcessRunMethodPhaseRunMethodExit;
|
|
|
|
}
|
|
|
|
} else if (BuildRequest->RunRequest.Flags & RUN_REQUEST_CHECK_WAKE_COUNT) {
|
|
|
|
//
|
|
// Do we need to check the Wake count?
|
|
//
|
|
if (deviceExtension->PowerInfo.WakeSupportCount == 0) {
|
|
|
|
//
|
|
// Nothing to do
|
|
//
|
|
goto ACPIBuildProcessRunMethodPhaseRunMethodExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup the arguments that we will pass to the method
|
|
//
|
|
RtlZeroMemory( objData, sizeof(OBJDATA) );
|
|
objData[0].uipDataValue = DATAVALUE_ONE;
|
|
objData[0].dwDataType = OBJTYPE_INTDATA;
|
|
|
|
//
|
|
// Remember that we have 1 argument
|
|
//
|
|
args = &objData[0];
|
|
numArgs = 1;
|
|
|
|
} else if (BuildRequest->RunRequest.Flags & RUN_REQUEST_REG_METHOD_ON ||
|
|
BuildRequest->RunRequest.Flags & RUN_REQUEST_REG_METHOD_OFF) {
|
|
|
|
//
|
|
// First thing is to make sure that we will never recurse past a pci
|
|
// PCI-PCI bridge
|
|
//
|
|
BuildRequest->RunRequest.Flags |= RUN_REQUEST_STOP_AT_BRIDGES;
|
|
|
|
//
|
|
// Next is that we have to initialize the arguments that we will
|
|
// pass to the function. For historical reasons, we will only
|
|
// pass in a REGSPACE_PCIFCFG registration
|
|
//
|
|
RtlZeroMemory( objData, sizeof(objData) );
|
|
objData[0].uipDataValue = REGSPACE_PCICFG;
|
|
objData[0].dwDataType = OBJTYPE_INTDATA;
|
|
objData[1].dwDataType = OBJTYPE_INTDATA;
|
|
if (BuildRequest->RunRequest.Flags & RUN_REQUEST_REG_METHOD_ON) {
|
|
|
|
objData[1].uipDataValue = 1;
|
|
|
|
} else {
|
|
|
|
objData[1].uipDataValue = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we have two arguments
|
|
//
|
|
args = &objData[0];
|
|
numArgs = 2;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we are running this control method
|
|
//
|
|
BuildRequest->CurrentObject = nsObj;
|
|
|
|
//
|
|
// Run the control method
|
|
//
|
|
status = AMLIAsyncEvalObject(
|
|
nsObj,
|
|
NULL,
|
|
numArgs,
|
|
args,
|
|
ACPIBuildCompleteMustSucceed,
|
|
BuildRequest
|
|
);
|
|
|
|
ACPIBuildProcessRunMethodPhaseRunMethodExit:
|
|
|
|
//
|
|
// What happened
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessRunMethodPhaseRunMethod: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Common code to handle the result of the 'Get' routine
|
|
//
|
|
if (status != STATUS_PENDING) {
|
|
|
|
ACPIBuildCompleteMustSucceed(
|
|
nsObj,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessSynchronizationList(
|
|
IN PLIST_ENTRY ListEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks at the elements in the synchronize list and
|
|
determines if the can be completed
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN allWorkComplete = TRUE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PLIST_ENTRY currentEntry = ListEntry->Flink;
|
|
|
|
while (currentEntry != ListEntry) {
|
|
|
|
//
|
|
// Turn into a build request
|
|
//
|
|
buildRequest = CONTAINING_RECORD(
|
|
currentEntry,
|
|
ACPI_BUILD_REQUEST,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// Set the temp pointer to the next element
|
|
//
|
|
currentEntry = currentEntry->Flink;
|
|
|
|
//
|
|
// Is the list pointed by this entry empty?
|
|
//
|
|
if (!IsListEmpty( (buildRequest->SynchronizeRequest.SynchronizeListEntry) ) ) {
|
|
|
|
allWorkComplete = FALSE;
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// Let the world know
|
|
//
|
|
deviceExtension = (PDEVICE_EXTENSION) buildRequest->BuildContext;
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildProcessSynchronizationList(%4s) = %08lx\n",
|
|
buildRequest->SynchronizeRequest.SynchronizeMethodNameAsUchar,
|
|
status
|
|
) );
|
|
|
|
//
|
|
// Complete the request
|
|
//
|
|
ACPIBuildProcessGenericComplete( buildRequest );
|
|
|
|
} // while
|
|
|
|
//
|
|
// Have we completed all of our work?
|
|
//
|
|
return (allWorkComplete ? STATUS_SUCCESS : STATUS_PENDING );
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildProcessThermalZonePhase0(
|
|
IN PACPI_BUILD_REQUEST BuildRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is run after we have build the thermal zone extension
|
|
|
|
Arguments:
|
|
|
|
BuildRequest - The request that we are processing
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION thermalExtension = (PDEVICE_EXTENSION) BuildRequest->BuildContext;
|
|
PTHRM_INFO info;
|
|
|
|
//
|
|
// Remember to set a pointer to the next state
|
|
//
|
|
BuildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
|
|
//
|
|
// We need a pointer to the thermal info
|
|
//
|
|
info = thermalExtension->Thermal.Info;
|
|
|
|
//
|
|
// We need the _TMP object
|
|
//
|
|
info->TempMethod = ACPIAmliGetNamedChild(
|
|
thermalExtension->AcpiObject,
|
|
PACKED_TMP
|
|
);
|
|
if (info->TempMethod == NULL) {
|
|
|
|
//
|
|
// If we don't have one... bugcheck
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_REQUIRED_METHOD_NOT_PRESENT,
|
|
(ULONG_PTR) thermalExtension,
|
|
PACKED_TMP,
|
|
0
|
|
);
|
|
goto ACPIBuildProcessThermalZonePhase0Exit;
|
|
|
|
}
|
|
|
|
ACPIBuildProcessThermalZonePhase0Exit:
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
thermalExtension,
|
|
"ACPIBuildProcessThermalZonePhase0: Status = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
//
|
|
// We won't actually need to call the interpreter, but we will call
|
|
// the generic callback so that we don't have to duplicate code
|
|
//
|
|
ACPIBuildCompleteGeneric(
|
|
NULL,
|
|
status,
|
|
NULL,
|
|
BuildRequest
|
|
);
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildDockExtension(
|
|
IN PNSOBJ CurrentObject,
|
|
IN PDEVICE_EXTENSION ParentDeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a device for CurrentObject, if it is an NameSpace
|
|
object that ACPI might be interested as, and links into the tree of
|
|
ParentDeviceExtension
|
|
|
|
Argument Description:
|
|
|
|
CurrentObject - The object that we are current interested in
|
|
ParentDeviceExtension - Where to link the deviceExtension into
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_NO_SUCH_DEVICE;
|
|
PDEVICE_EXTENSION deviceExtension = NULL;
|
|
PUCHAR deviceID = NULL;
|
|
PUCHAR instanceID = NULL;
|
|
|
|
//
|
|
// Build the device extension
|
|
//
|
|
status = ACPIBuildDeviceExtension(
|
|
NULL,
|
|
ParentDeviceExtension,
|
|
&deviceExtension
|
|
);
|
|
if (!NT_SUCCESS(status) || deviceExtension == NULL) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, we care about this device, so we will allocate some
|
|
// memory for the deviceID, which we will build this off the ACPI node
|
|
// name.
|
|
//
|
|
deviceID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
21,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (deviceID == NULL) {
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
"ACPIBuildDockExtension: Cannot allocate 0x%04x "
|
|
"bytes for deviceID\n",
|
|
21
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildDockExtensionExit;
|
|
|
|
}
|
|
|
|
//
|
|
// The format for a deviceID is
|
|
// ACPI\DockDevice
|
|
// the ACPI node name will form the instance ID
|
|
strcpy( deviceID, "ACPI\\DockDevice") ;
|
|
deviceExtension->DeviceID = deviceID;
|
|
|
|
//
|
|
// Form the instance ID
|
|
//
|
|
status = ACPIAmliBuildObjectPathname(CurrentObject, &instanceID) ;
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
deviceExtension,
|
|
"ACPIBuildDockExtension: Path = %08lx\n",
|
|
status
|
|
) );
|
|
goto ACPIBuildDockExtensionExit;
|
|
|
|
}
|
|
deviceExtension->InstanceID = instanceID;
|
|
|
|
//
|
|
// And make sure we are pointed to the correct docking node
|
|
//
|
|
deviceExtension->Dock.CorrospondingAcpiDevice =
|
|
(PDEVICE_EXTENSION) CurrentObject->Context ;
|
|
|
|
//
|
|
// By default, we update profiles only on eject
|
|
//
|
|
deviceExtension->Dock.ProfileDepartureStyle = PDS_UPDATE_ON_EJECT;
|
|
|
|
//
|
|
// If we are booting, or the device has just come back we assume _DCK has
|
|
// already been ran if we find the device with _STA == present. We will
|
|
// only override this assumption if Notify(Dock, 0) is called.
|
|
//
|
|
deviceExtension->Dock.IsolationState = IS_UNKNOWN;
|
|
|
|
//
|
|
// Make sure that we remember that we are a dock
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_TYPE_NOT_FOUND |
|
|
DEV_PROP_UID | DEV_PROP_FIXED_UID |
|
|
DEV_PROP_HID | DEV_PROP_FIXED_HID |
|
|
DEV_PROP_NO_OBJECT | DEV_PROP_DOCK | DEV_CAP_RAW,
|
|
FALSE
|
|
);
|
|
|
|
ACPIBuildDockExtensionExit:
|
|
|
|
//
|
|
// Free any resources that we don't need because we failed. Note
|
|
// that the way this is structured, we won't have to acquire a spinlock
|
|
// since by the time we attempt to link in the tree, we cannot fail
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
deviceExtension,
|
|
"ACPIBuildDockExtension: = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
if (instanceID != NULL ) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID),
|
|
TRUE
|
|
);
|
|
ExFreePool( instanceID );
|
|
deviceExtension->InstanceID = NULL;
|
|
|
|
}
|
|
if (deviceID != NULL) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID),
|
|
TRUE
|
|
);
|
|
ExFreePool( deviceID );
|
|
deviceExtension->DeviceID = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we failed init
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(deviceExtension->Flags),
|
|
DEV_PROP_FAILED_INIT,
|
|
TRUE
|
|
);
|
|
|
|
} else {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
deviceExtension,
|
|
"ACPIBuildDockExtension: = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildRegRequest(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PACPI_BUILD_CALLBACK CallBack
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a device is turned on, and we need to tell
|
|
the AML that the regionspace behind it are available
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The target device object
|
|
Irp - The target irp
|
|
CallBack - The routine to call when done
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
DEVICE_POWER_STATE deviceState;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
|
ULONG methodFlags;
|
|
|
|
//
|
|
// Grab the requested device state and power action
|
|
//
|
|
deviceState = irpStack->Parameters.Power.State.DeviceState;
|
|
|
|
//
|
|
// Let the user know what is going on
|
|
//
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_POWER,
|
|
deviceExtension,
|
|
"(0x%08lx): ACPIBuildRegRequest - Handle D%d\n",
|
|
Irp,
|
|
(deviceState - PowerDeviceD0)
|
|
) );
|
|
|
|
//
|
|
// Do we need to mark the irp as pending?
|
|
//
|
|
if (Irp->PendingReturned) {
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
}
|
|
|
|
//
|
|
// Lets us look at the current status code for the request. On error,
|
|
// we will just call the completion right now, and it is responsible
|
|
// for doing the 'right' thing
|
|
//
|
|
status = Irp->IoStatus.Status;
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Call the completion routine and return
|
|
//
|
|
if (*CallBack != NULL ) {
|
|
|
|
(*CallBack)(
|
|
deviceExtension,
|
|
Irp,
|
|
status
|
|
);
|
|
|
|
}
|
|
return status;
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate the flags that we will use
|
|
//
|
|
methodFlags = (RUN_REQUEST_CHECK_STATUS | RUN_REQUEST_RECURSIVE);
|
|
if (deviceState == PowerDeviceD0) {
|
|
|
|
methodFlags |= RUN_REQUEST_REG_METHOD_ON;
|
|
|
|
} else {
|
|
|
|
methodFlags |= RUN_REQUEST_REG_METHOD_OFF;
|
|
|
|
}
|
|
|
|
//
|
|
// Queue the request --- this function will always return
|
|
// MORE_PROCESSING_REQUIRED instead of PENDING, so we don't have
|
|
// to mess with it
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
status = ACPIBuildRunMethodRequest(
|
|
deviceExtension,
|
|
CallBack,
|
|
(PVOID) Irp,
|
|
PACKED_REG,
|
|
methodFlags,
|
|
TRUE
|
|
);
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
if (status == STATUS_PENDING) {
|
|
|
|
status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildRegOffRequest(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PACPI_BUILD_CALLBACK CallBack
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a device is turned off, and we need to tell
|
|
the AML that the regionspace behind it are not available
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The target device object
|
|
Irp - The target irp
|
|
CallBack - The routine to call when done
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
return ACPIBuildRegRequest( DeviceObject, Irp, CallBack );
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildRegOnRequest(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PACPI_BUILD_CALLBACK CallBack
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a device is turned on, and we need to tell
|
|
the AML that the regionspace behind it are now available
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The target device object
|
|
Irp - The target irp
|
|
CallBack - The routine to call when done
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
ACPIBuildRegRequest( DeviceObject, Irp, CallBack );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildRunMethodRequest(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PACPI_BUILD_CALLBACK CallBack,
|
|
IN PVOID CallBackContext,
|
|
IN ULONG MethodName,
|
|
IN ULONG MethodFlags,
|
|
IN BOOLEAN RunDPC
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to request that a control method be run
|
|
recursively on the device tree
|
|
|
|
Note: AcpiDeviceTreeLock must be held to call this function
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device extension to run the method on
|
|
MethodName - The name of the method to run
|
|
RunDpc - Should we run the dpc?
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
PACPI_BUILD_REQUEST syncRequest;
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Allocate a buildRequest structure
|
|
//
|
|
buildRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (buildRequest == NULL) {
|
|
|
|
if (CallBack != NULL) {
|
|
|
|
(*CallBack)(
|
|
DeviceExtension,
|
|
CallBackContext,
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
);
|
|
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// Do we need to have the 2nd buildrequest structure?
|
|
//
|
|
if (CallBack != NULL) {
|
|
|
|
syncRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (syncRequest == NULL) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
buildRequest
|
|
);
|
|
(*CallBack)(
|
|
DeviceExtension,
|
|
CallBackContext,
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the current reference is 0, that means that someone else beat
|
|
// use to the device extension that that we *CANNOT* touch it
|
|
//
|
|
if (DeviceExtension->ReferenceCount == 0) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
buildRequest
|
|
);
|
|
if (CallBack != NULL) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
syncRequest
|
|
);
|
|
(*CallBack)(
|
|
DeviceExtension,
|
|
CallBackContext,
|
|
STATUS_DEVICE_REMOVED
|
|
);
|
|
|
|
}
|
|
return STATUS_DEVICE_REMOVED;
|
|
|
|
} else {
|
|
|
|
InterlockedIncrement( &(DeviceExtension->ReferenceCount) );
|
|
if (CallBack != NULL) {
|
|
|
|
//
|
|
// Grab second reference
|
|
//
|
|
InterlockedIncrement( &(DeviceExtension->ReferenceCount) );
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( buildRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
buildRequest->Signature = ACPI_SIGNATURE;
|
|
buildRequest->TargetListEntry = &AcpiBuildRunMethodList;
|
|
buildRequest->WorkDone = WORK_DONE_STEP_0;
|
|
buildRequest->Status = STATUS_SUCCESS;
|
|
buildRequest->BuildContext = DeviceExtension;
|
|
buildRequest->RunRequest.ControlMethodName = MethodName;
|
|
buildRequest->RunRequest.Flags = MethodFlags;
|
|
buildRequest->Flags = BUILD_REQUEST_VALID_TARGET |
|
|
BUILD_REQUEST_RUN |
|
|
BUILD_REQUEST_RELEASE_REFERENCE;
|
|
|
|
//
|
|
// Do we have to call the callback? If so, we need a 2nd request to
|
|
// queue up to the synchronize list
|
|
//
|
|
if (CallBack != NULL) {
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( syncRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
syncRequest->Signature = ACPI_SIGNATURE;
|
|
syncRequest->TargetListEntry = &AcpiBuildSynchronizationList;
|
|
syncRequest->WorkDone = WORK_DONE_STEP_0;
|
|
syncRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
syncRequest->Status = STATUS_SUCCESS;
|
|
syncRequest->CallBack = CallBack;
|
|
syncRequest->CallBackContext = CallBackContext;
|
|
syncRequest->BuildContext = DeviceExtension;
|
|
syncRequest->SynchronizeRequest.SynchronizeListEntry =
|
|
&AcpiBuildRunMethodList;
|
|
syncRequest->SynchronizeRequest.SynchronizeMethodName =
|
|
MethodName;
|
|
syncRequest->Flags = BUILD_REQUEST_VALID_TARGET |
|
|
BUILD_REQUEST_SYNC |
|
|
BUILD_REQUEST_RELEASE_REFERENCE;
|
|
syncRequest->SynchronizeRequest.Flags = SYNC_REQUEST_HAS_METHOD;
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, we need the spinlock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Add this to the list
|
|
//
|
|
InsertTailList(
|
|
&AcpiBuildQueueList,
|
|
&(buildRequest->ListEntry)
|
|
);
|
|
|
|
if (CallBack != NULL) {
|
|
|
|
InsertTailList(
|
|
&AcpiBuildQueueList,
|
|
&(syncRequest->ListEntry)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Do we need to queue up the DPC?
|
|
//
|
|
if (RunDPC && !AcpiBuildDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildSurpriseRemovedExtension(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the system wants to turn the above
|
|
extension into a surprised removed one
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The extension that is being surprised removed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
PDEVICE_EXTENSION dockExtension;
|
|
PDEVICE_EXTENSION parentExtension, childExtension;
|
|
EXTENSIONLIST_ENUMDATA eled;
|
|
|
|
//
|
|
// This device might have a corrosponding fake extension. Find out now - if
|
|
// it exists we must nuke it.
|
|
//
|
|
dockExtension = ACPIDockFindCorrespondingDock( DeviceExtension );
|
|
|
|
if (dockExtension) {
|
|
|
|
//
|
|
// We have a fake dock, nuke it too since it's underlying hardware is
|
|
// gone.
|
|
//
|
|
dockExtension->DeviceState = SurpriseRemoved;
|
|
ACPIBuildSurpriseRemovedExtension( dockExtension );
|
|
}
|
|
|
|
ACPIExtListSetupEnum(
|
|
&eled,
|
|
&(DeviceExtension->ChildDeviceList),
|
|
&AcpiDeviceTreeLock,
|
|
SiblingDeviceList,
|
|
WALKSCHEME_REFERENCE_ENTRIES
|
|
);
|
|
|
|
for(childExtension = ACPIExtListStartEnum(&eled);
|
|
ACPIExtListTestElement(&eled, TRUE);
|
|
childExtension = ACPIExtListEnumNext(&eled)) {
|
|
|
|
ACPIBuildSurpriseRemovedExtension(childExtension);
|
|
}
|
|
|
|
//
|
|
// We also want to flush the power queue to insure that any events
|
|
// dealing with the removed object go away as fast as possible...
|
|
//
|
|
ACPIDevicePowerFlushQueue( DeviceExtension );
|
|
|
|
//
|
|
// At this point, we don't think the device is coming back, so we
|
|
// need to fully remove this extension. The first step to do that
|
|
// is mark the extension as appropriate, and to do that, we need
|
|
// the device spin lock
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
|
|
//
|
|
// Clear the flags for this extension
|
|
//
|
|
if (DeviceExtension->Flags & DEV_TYPE_PDO) {
|
|
|
|
ACPIInternalUpdateFlags( &(DeviceExtension->Flags), DEV_MASK_TYPE, TRUE );
|
|
ACPIInternalUpdateFlags(
|
|
&(DeviceExtension->Flags),
|
|
(DEV_TYPE_PDO | DEV_TYPE_SURPRISE_REMOVED | DEV_PROP_NO_OBJECT | DEV_TYPE_NOT_ENUMERATED),
|
|
FALSE
|
|
);
|
|
DeviceExtension->DispatchTable = &AcpiSurpriseRemovedPdoIrpDispatch;
|
|
|
|
} else if (DeviceExtension->Flags & DEV_TYPE_FILTER) {
|
|
|
|
ACPIInternalUpdateFlags( &(DeviceExtension->Flags), DEV_MASK_TYPE, TRUE );
|
|
ACPIInternalUpdateFlags(
|
|
&(DeviceExtension->Flags),
|
|
(DEV_TYPE_FILTER | DEV_TYPE_SURPRISE_REMOVED | DEV_PROP_NO_OBJECT | DEV_TYPE_NOT_ENUMERATED),
|
|
FALSE
|
|
);
|
|
DeviceExtension->DispatchTable = &AcpiSurpriseRemovedFilterIrpDispatch;
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, we are going to have to make a call ---
|
|
// do we re-build the original device extension in the tree
|
|
// or do we forget about it. We have to forget about it if the
|
|
// table is being unloaded. We need to make this decision while
|
|
// we still have a pointer to the parent extension...
|
|
//
|
|
if (!(DeviceExtension->Flags & DEV_PROP_UNLOADING) ) {
|
|
|
|
//
|
|
// Set the bit to cause the parent to rebuild missing
|
|
// children on QDR
|
|
//
|
|
parentExtension = DeviceExtension->ParentExtension;
|
|
if (parentExtension) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(parentExtension->Flags),
|
|
DEV_PROP_REBUILD_CHILDREN,
|
|
FALSE
|
|
);
|
|
|
|
if (DeviceExtension->AcpiObject &&
|
|
ACPIDockIsDockDevice(DeviceExtension->AcpiObject)) {
|
|
|
|
ASSERT(parentExtension->PhysicalDeviceObject != NULL);
|
|
|
|
//
|
|
// This will cause us to rebuild this extension afterwards. We
|
|
// need this because notify attempts on docks require fully
|
|
// built and processed device extensions.
|
|
//
|
|
IoInvalidateDeviceRelations(
|
|
parentExtension->PhysicalDeviceObject,
|
|
SingleBusRelations
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove this extension from the tree. This will nuke the pointer
|
|
// to the parent extension (that's the link that gets cut from the
|
|
// tree)
|
|
//
|
|
ACPIInitRemoveDeviceExtension( DeviceExtension );
|
|
|
|
//
|
|
// Remember to make sure that the ACPI Object no longer points to this
|
|
// device extension
|
|
//
|
|
if (DeviceExtension->AcpiObject) {
|
|
|
|
DeviceExtension->AcpiObject->Context = NULL;
|
|
}
|
|
|
|
//
|
|
// Are we a thermal zone?
|
|
//
|
|
if (DeviceExtension->Flags & DEV_CAP_THERMAL_ZONE) {
|
|
|
|
//
|
|
// Do Some Clean-up by flushing all the currently queued requests
|
|
//
|
|
ACPIThermalCompletePendingIrps(
|
|
DeviceExtension,
|
|
DeviceExtension->Thermal.Info
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildSynchronizationRequest(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PACPI_BUILD_CALLBACK CallBack,
|
|
IN PVOID CallBackContext,
|
|
IN PLIST_ENTRY SynchronizeListEntry,
|
|
IN BOOLEAN RunDPC
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the system wants to know when the DPC routine
|
|
has been completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - This is the device extension that we are
|
|
typically interested in. Usually, it will be the
|
|
root node
|
|
CallBack - The function to call when done
|
|
CallBackContext - The argument to pass to that function
|
|
Event - The event to notify when done
|
|
RunDpc - Should we run the dpc?
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
|
|
//
|
|
// Allocate a buildRequest structure
|
|
//
|
|
buildRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (buildRequest == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// We need the device tree lock while we look at the device
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
|
|
//
|
|
// If the current reference is 0, that means that someone else beat
|
|
// use to the device extension that that we *CANNOT* touch it
|
|
//
|
|
if (DeviceExtension->ReferenceCount == 0) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
buildRequest
|
|
);
|
|
return STATUS_DEVICE_REMOVED;
|
|
|
|
} else {
|
|
|
|
InterlockedIncrement( &(DeviceExtension->ReferenceCount) );
|
|
|
|
}
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( buildRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
buildRequest->Signature = ACPI_SIGNATURE;
|
|
buildRequest->TargetListEntry = &AcpiBuildSynchronizationList;
|
|
buildRequest->WorkDone = WORK_DONE_STEP_0;
|
|
buildRequest->NextWorkDone = WORK_DONE_COMPLETE;
|
|
buildRequest->Status = STATUS_SUCCESS;
|
|
buildRequest->CallBack = CallBack;
|
|
buildRequest->CallBackContext = CallBackContext;
|
|
buildRequest->BuildContext = DeviceExtension;
|
|
buildRequest->SynchronizeRequest.SynchronizeListEntry =
|
|
SynchronizeListEntry;
|
|
buildRequest->Flags = BUILD_REQUEST_VALID_TARGET |
|
|
BUILD_REQUEST_SYNC |
|
|
BUILD_REQUEST_RELEASE_REFERENCE;
|
|
|
|
//
|
|
// Done looking at the device
|
|
//
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
//
|
|
// At this point, we need the build queue spinlock
|
|
//
|
|
KeAcquireSpinLock( &AcpiBuildQueueLock, &oldIrql );
|
|
|
|
//
|
|
// Add this to the list. We add the request to the head
|
|
// of the list because we want to guarantee a LIFO ordering
|
|
//
|
|
InsertHeadList(
|
|
&AcpiBuildQueueList,
|
|
&(buildRequest->ListEntry)
|
|
);
|
|
|
|
//
|
|
// Do we need to queue up the DPC?
|
|
//
|
|
if (RunDPC && !AcpiBuildDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLock( &AcpiBuildQueueLock, oldIrql );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildThermalZoneExtension(
|
|
IN PNSOBJ ThermalObject,
|
|
IN PDEVICE_EXTENSION ParentExtension,
|
|
IN PDEVICE_EXTENSION *ResultExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Since we leverage ACPIBuildDeviceExtension for the core of the thermal
|
|
extension, we don't have much to do here. However, we are responsible
|
|
for making sure that we do tasks that don't require calling the interpreter,
|
|
and a unique to the ThermalZone here
|
|
|
|
N.B. This function is called with AcpiDeviceTreeLock being held
|
|
|
|
Arguments:
|
|
|
|
ThermalObject - The object we care about
|
|
ParentExtension - Who our parent is
|
|
ResultExtension - Where to store the extension that we build
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION thermalExtension;
|
|
PTHRM_INFO info;
|
|
|
|
//
|
|
// Build the extension
|
|
//
|
|
status = ACPIBuildDeviceExtension(
|
|
ThermalObject,
|
|
ParentExtension,
|
|
ResultExtension
|
|
);
|
|
if (!NT_SUCCESS(status) || *ResultExtension == NULL) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
thermalExtension = *ResultExtension;
|
|
|
|
//
|
|
// Make sure to remember that this is in fact a thermal zone
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(thermalExtension->Flags),
|
|
(DEV_CAP_THERMAL_ZONE | DEV_MASK_THERMAL | DEV_CAP_RAW | DEV_CAP_NO_STOP),
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Allocate the additional thermal device storage
|
|
//
|
|
info = thermalExtension->Thermal.Info = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(THRM_INFO),
|
|
ACPI_THERMAL_POOLTAG
|
|
);
|
|
if (thermalExtension->Thermal.Info == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
thermalExtension,
|
|
"ACPIBuildThermalZoneExtension: failed to allocate %08 bytes\n",
|
|
sizeof(THRM_INFO)
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildThermalZoneExtensionExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that the memory is freshly scrubbed
|
|
//
|
|
RtlZeroMemory( thermalExtension->Thermal.Info, sizeof(THRM_INFO) );
|
|
|
|
//
|
|
// Allocate memory for the HID
|
|
//
|
|
thermalExtension->DeviceID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
strlen(ACPIThermalZoneId) + 1,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (thermalExtension->DeviceID == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
thermalExtension,
|
|
"ACPIBuildThermalZoneExtension: failed to allocate %08 bytes\n",
|
|
strlen(ACPIThermalZoneId) + 1
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildThermalZoneExtensionExit;
|
|
|
|
}
|
|
RtlCopyMemory(
|
|
thermalExtension->DeviceID,
|
|
ACPIThermalZoneId,
|
|
strlen(ACPIThermalZoneId) + 1
|
|
);
|
|
|
|
//
|
|
// Allocate memory for the UID
|
|
//
|
|
thermalExtension->InstanceID = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
5,
|
|
ACPI_STRING_POOLTAG
|
|
);
|
|
if (thermalExtension->InstanceID == NULL) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_CRITICAL,
|
|
thermalExtension,
|
|
"ACPIBuildThermalZoneExtension: failed to allocate %08 bytes\n",
|
|
5
|
|
) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ACPIBuildThermalZoneExtensionExit;
|
|
|
|
}
|
|
RtlCopyMemory(
|
|
thermalExtension->InstanceID,
|
|
(PUCHAR) &(thermalExtension->AcpiObject->dwNameSeg),
|
|
4
|
|
);
|
|
thermalExtension->InstanceID[4] = '\0';
|
|
|
|
//
|
|
// Set the flags for the work that we have just done
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(thermalExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID | DEV_PROP_UID | DEV_PROP_FIXED_UID),
|
|
FALSE
|
|
);
|
|
|
|
ACPIBuildThermalZoneExtensionExit:
|
|
|
|
//
|
|
// Handle the case where we might have failed
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_FAILURE,
|
|
thermalExtension,
|
|
"ACPIBuildThermalZoneExtension: = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
if (thermalExtension->InstanceID != NULL) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(thermalExtension->Flags),
|
|
(DEV_PROP_UID | DEV_PROP_FIXED_UID),
|
|
TRUE
|
|
);
|
|
ExFreePool( thermalExtension->InstanceID );
|
|
thermalExtension->InstanceID = NULL;
|
|
|
|
}
|
|
|
|
if (thermalExtension->DeviceID != NULL) {
|
|
|
|
ACPIInternalUpdateFlags(
|
|
&(thermalExtension->Flags),
|
|
(DEV_PROP_HID | DEV_PROP_FIXED_HID),
|
|
TRUE
|
|
);
|
|
ExFreePool( thermalExtension->DeviceID );
|
|
thermalExtension->DeviceID = NULL;
|
|
|
|
}
|
|
|
|
if (thermalExtension->Thermal.Info != NULL) {
|
|
|
|
ExFreePool( thermalExtension->Thermal.Info );
|
|
thermalExtension->Thermal.Info = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we failed init
|
|
//
|
|
ACPIInternalUpdateFlags(
|
|
&(thermalExtension->Flags),
|
|
DEV_PROP_FAILED_INIT,
|
|
TRUE
|
|
);
|
|
|
|
} else {
|
|
|
|
ACPIDevPrint( (
|
|
ACPI_PRINT_LOADING,
|
|
thermalExtension,
|
|
"ACPIBuildThermalZoneExtension: = %08lx\n",
|
|
status
|
|
) );
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ACPIBuildThermalZoneRequest(
|
|
IN PDEVICE_EXTENSION ThermalExtension,
|
|
IN PACPI_BUILD_CALLBACK CallBack,
|
|
IN PVOID CallBackContext,
|
|
IN BOOLEAN RunDPC
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a thermal zone is ready to be filled in.
|
|
This routine creates a request which is enqueued. When the DPC is fired,
|
|
the request will be processed
|
|
|
|
Note: AcpiDeviceTreeLock must be held to call this function
|
|
|
|
Arguments:
|
|
|
|
ThermalExtension - The thermal zone to process
|
|
CallBack - The function to call when done
|
|
CallBackContext - The argument to pass to that function
|
|
RunDPC - Should we enqueue the DPC immediately (if it is not
|
|
running?)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PACPI_BUILD_REQUEST buildRequest;
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Allocate a buildRequest structure
|
|
//
|
|
buildRequest = ExAllocateFromNPagedLookasideList(
|
|
&BuildRequestLookAsideList
|
|
);
|
|
if (buildRequest == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
//
|
|
// If the current reference is 0, that means that someone else beat
|
|
// use to the device extension that that we *CANNOT* touch it
|
|
//
|
|
if (ThermalExtension->ReferenceCount == 0) {
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&BuildRequestLookAsideList,
|
|
buildRequest
|
|
);
|
|
return STATUS_DEVICE_REMOVED;
|
|
|
|
} else {
|
|
|
|
InterlockedIncrement( &(ThermalExtension->ReferenceCount) );
|
|
|
|
}
|
|
|
|
//
|
|
// Fill in the structure
|
|
//
|
|
RtlZeroMemory( buildRequest, sizeof(ACPI_BUILD_REQUEST) );
|
|
buildRequest->Signature = ACPI_SIGNATURE;
|
|
buildRequest->TargetListEntry = &AcpiBuildThermalZoneList;
|
|
buildRequest->WorkDone = WORK_DONE_STEP_0;
|
|
buildRequest->Status = STATUS_SUCCESS;
|
|
buildRequest->CallBack = CallBack;
|
|
buildRequest->CallBackContext = CallBackContext;
|
|
buildRequest->BuildContext = ThermalExtension;
|
|
buildRequest->Flags = BUILD_REQUEST_VALID_TARGET |
|
|
BUILD_REQUEST_RELEASE_REFERENCE;
|
|
|
|
//
|
|
// At this point, we need the spinlock
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Add this to the list
|
|
//
|
|
InsertTailList(
|
|
&AcpiBuildQueueList,
|
|
&(buildRequest->ListEntry)
|
|
);
|
|
|
|
//
|
|
// Do we need to queue up the DPC?
|
|
//
|
|
if (RunDPC && !AcpiBuildDpcRunning) {
|
|
|
|
KeInsertQueueDpc( &AcpiBuildDpc, 0, 0 );
|
|
|
|
}
|
|
|
|
//
|
|
// Done with the lock
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel( &AcpiBuildQueueLock );
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|