/*++ 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; }