/*++ Copyright (c) 1998 Microsoft Corporation Module Name: osnotify.c Abstract: This module implements all the callbacks that are NT specific from the AML Interpreter Environment Kernel mode only Revision History: 01-Mar-98 Initial Revision [split from callback.c] --*/ #include "pch.h" // // Make sure that we have permanent storage for our fatal error context // ACPI_FATAL_ERROR_CONTEXT AcpiFatalContext; // // Spinlock to protect the entire thing KSPIN_LOCK AcpiFatalLock; // // Is there an outstanding Fatal Error Context? // BOOLEAN AcpiFatalOutstanding; NTSTATUS EXPORT OSNotifyCreate( IN ULONG ObjType, IN PNSOBJ AcpiObject ) /*++ Routine Description: This routine is called whenever a new object is created by the interpreter This routine dispatches based on what object type it is. Arguments: ObjType - What type of object it is AcpiObject - Pointer to the new ACPI Object Return Value: NTSTATUS --*/ { KIRQL oldIrql; NTSTATUS status = STATUS_SUCCESS; ASSERT( AcpiObject != NULL ); // // We will touch the device tree. So we need to hold the correct lock // KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql ); switch(ObjType) { case OBJTYPE_DEVICE: status = OSNotifyCreateDevice( AcpiObject, 0 ); break; case OBJTYPE_OPREGION: status = OSNotifyCreateOperationRegion( AcpiObject ); break; case OBJTYPE_POWERRES: status = OSNotifyCreatePowerResource( AcpiObject ); break; case OBJTYPE_PROCESSOR: status = OSNotifyCreateProcessor( AcpiObject, 0 ); break; case OBJTYPE_THERMALZONE: status = OSNotifyCreateThermalZone( AcpiObject, 0 ); break; default: ACPIPrint( ( ACPI_PRINT_WARNING, "OSNotifyCreate: received unhandled type %x\n", ObjType ) ); status = STATUS_SUCCESS; } // // Done with this lock // KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql ); // // What happened? // ACPIPrint( ( ACPI_PRINT_LOADING, "OSNotifyCreate: %p (%s) = %08lx\n", AcpiObject, ACPIAmliNameObject( AcpiObject ), status ) ); // // Done --- Always succeed // return STATUS_SUCCESS; } NTSTATUS OSNotifyCreateDevice( IN PNSOBJ AcpiObject, IN ULONGLONG OptionalFlags ) /*++ Routine Description: This routine is called whenever a new device appears. This routine is callable at DispatchLevel. Arguments: AcpiObject - Pointer to new ACPI Object OptionalFlags - Properties of the Device Extension that should be set when its created. Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension = NULL; PDEVICE_EXTENSION parentExtension; PNSOBJ parentObject; ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); ASSERT( AcpiObject != NULL); // // First, we need a pointer to the parent node // parentObject = AcpiObject->pnsParent; ASSERT( parentObject != NULL ); // // Grab the device extension associated with the parent. We need // this information to help link the parent properly into the tree // parentExtension = (PDEVICE_EXTENSION) parentObject->Context; if (parentExtension == NULL) { // // In this case, we can assume that the parent extension is the root // device extension. // parentExtension = RootDeviceExtension; } ASSERT( parentExtension != NULL ); // // Now build an extension for the node // status = ACPIBuildDeviceExtension( AcpiObject, parentExtension, &deviceExtension ); if (deviceExtension == NULL) { status = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS(status)) { // // Incremement the reference count on the node. We do this because // we are going to be doing work (which will take a long time // to complete, anyways), and we don't want to hold the lock for that // entire time. If we incr the reference count, then we guarantee that // no one can come along and kick the feet out from underneath us // InterlockedIncrement( &(deviceExtension->ReferenceCount) ); } // // What happend to the creation of the extension? // if (!NT_SUCCESS(status)) { // // We should have succeeded at whatever we are doing --- so this is // a bad place to be // ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreateDevice: NSObj %p Failed %08lx\n", AcpiObject, status ) ); goto OSNotifyCreateDeviceExit; } // // Set the optional flags if there are any // ACPIInternalUpdateFlags( &(deviceExtension->Flags), OptionalFlags, FALSE ); // // Make sure to queue the request // status = ACPIBuildDeviceRequest( deviceExtension, NULL, NULL, FALSE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreateDevice: ACPIBuildDeviceRequest(%p) = %08lx\n", deviceExtension, status ) ); goto OSNotifyCreateDeviceExit; } OSNotifyCreateDeviceExit: // // There is some work that will be done later // return status; } NTSTATUS OSNotifyCreateOperationRegion( IN PNSOBJ AcpiObject ) /*++ Routine Description: This routine is called whenever a new operation region is created. This routine is callable at DispatchLevel. Arguments: AcpiObject - Pointer to the new ACPI Operation Region Object Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION parentExtension; PNSOBJ parentObject; POPREGIONOBJ opRegion; // // Sanity Check // ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); ASSERT( AcpiObject != NULL ); ASSERT( NSGETOBJTYPE(AcpiObject) == OBJTYPE_OPREGION ); ASSERT( AcpiObject->ObjData.pbDataBuff != NULL ); // // Get the OpRegion Object from the namespace object // opRegion = (POPREGIONOBJ) AcpiObject->ObjData.pbDataBuff; if (opRegion->bRegionSpace != REGSPACE_PCIBARTARGET) { // // This isn't a PCI Bar Target Operation Region, so there // is nothing to do // return STATUS_SUCCESS; } // // There are two cases to consider. The first case is the // one where the Operation Region is "static" in nature and // thus exists under some sort of device. The second case is // the one where the Operation Region is "dynamic" in nature // and thus exists under some sort of method. So, we want to // look at parent objects until we hit one that isn't a method // or is a device... // parentObject = AcpiObject->pnsParent; while (parentObject != NULL) { // // If the parent object is a method, then look at its parent // if (NSGETOBJTYPE(parentObject) == OBJTYPE_METHOD) { parentObject = parentObject->pnsParent; continue; } // // If the parent object isn't a device, then stop... // if (NSGETOBJTYPE(parentObject) != OBJTYPE_DEVICE) { break; } // // Grab the device extension (bad things happen if it doesn't // already exist // parentExtension = (PDEVICE_EXTENSION) parentObject->Context; if (parentExtension) { ACPIInternalUpdateFlags( &(parentExtension->Flags), DEV_CAP_PCI_BAR_TARGET, FALSE ); } break; } // // Done // return STATUS_SUCCESS; } NTSTATUS OSNotifyCreatePowerResource( IN PNSOBJ AcpiObject ) /*++ Routine Description: This routine is called whenever a new power resource appears. This routine is callable at DispatchLevel. Arguments: AcpiObject - Pointer to new ACPI Object Return Value: NTSTATUS --*/ { NTSTATUS status; PACPI_POWER_DEVICE_NODE powerNode; ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); ASSERT( AcpiObject != NULL); // // Build the power extension // status = ACPIBuildPowerResourceExtension( AcpiObject, &powerNode ); // // What happened? // if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreatePowerResource: %p = %08lx\n", AcpiObject, status ) ); goto OSNotifyCreatePowerResourceExit; } // // Make sure to request that this node gets processed // status = ACPIBuildPowerResourceRequest( powerNode, NULL, NULL, FALSE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreatePowerResource: " "ACPIBuildPowerResourceRequest(%p) = %08lx\n", powerNode, status ) ); goto OSNotifyCreatePowerResourceExit; } OSNotifyCreatePowerResourceExit: // // Done // return status; } NTSTATUS OSNotifyCreateProcessor( IN PNSOBJ AcpiObject, IN ULONGLONG OptionalFlags ) /*++ Routine Description: This routine is called whenever a new processor appears. This routine is callable at DispatchLevel. Arguments: AcpiObject - Pointer to the new ACPI object OptionalFlags - Properties of the Device Extension that should be set when its created. Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension = NULL; PDEVICE_EXTENSION parentExtension; PNSOBJ parentObject; UCHAR index = 0; ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); ASSERT( AcpiObject != NULL); // // Note: ProcessorList is now implicitly protected by the device tree // lock since we need to acquire that lock before calling this function // // while (ProcessorList[index] && index < ACPI_SUPPORTED_PROCESSORS) { index++; } // // We must make sure that the current entry is empty... // if (index >= ACPI_SUPPORTED_PROCESSORS || ProcessorList[index] != NULL) { return STATUS_UNSUCCESSFUL; } ACPIPrint( ( ACPI_PRINT_LOADING, "OSNotifyCreateProcessor: Processor Object #%x: %x\n", index+1, AcpiObject ) ); // // Remember that to store where the new processor object is located // ProcessorList[index] = AcpiObject; // // First, we need a pointer to the parent node // parentObject = AcpiObject->pnsParent; ASSERT( parentObject != NULL ); // // Grab the device extension associated with the parent. We need // this information to help link the parent properly into the tree // parentExtension = (PDEVICE_EXTENSION) parentObject->Context; if (parentExtension == NULL) { // // In this case, we can assume that the parent extension is the root // device extension. // parentExtension = RootDeviceExtension; } ASSERT( parentExtension != NULL ); // // Now build an extension for the node // status = ACPIBuildProcessorExtension( AcpiObject, parentExtension, &deviceExtension, index ); if (NT_SUCCESS(status)) { // // Incremement the reference count on the node. We do this because // we are going to be doing work (which will take a long time // to complete, anyways), and we don't want to hold the lock for that // entire time. If we incr the reference count, then we guarantee that // no one can come along and kick the feet out from underneath us // InterlockedIncrement( &(deviceExtension->ReferenceCount) ); } // // What happend to the creation of the extension? // if (!NT_SUCCESS(status)) { // // We should have succeeded at whatever we are doing --- so this is // a bad place to be // ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreateProcessor: NSObj %p Failed %08lx\n", AcpiObject, status ) ); goto OSNotifyCreateProcessorExit; } // // Set the optional flags if there are any // ACPIInternalUpdateFlags( &(deviceExtension->Flags), OptionalFlags, FALSE ); // // Make sure to queue the request // status = ACPIBuildProcessorRequest( deviceExtension, NULL, NULL, FALSE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreateProcessor: " "ACPIBuildProcessorRequest(%p) = %08lx\n", deviceExtension, status ) ); goto OSNotifyCreateProcessorExit; } OSNotifyCreateProcessorExit: // // There is some work that will be done later // return status; } NTSTATUS OSNotifyCreateThermalZone( IN PNSOBJ AcpiObject, IN ULONGLONG OptionalFlags ) /*++ Routine Description: This routine is called whenever a new thermal zone appears. This routine is callable at DispatchLevel. Arguments: AcpiObject - Pointer to new ACPI Object OptionalFlags - Properties of the Device Extension that should be set when its created. Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension = NULL; ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); ASSERT( AcpiObject != NULL); // // Now build an extension for the node // status = ACPIBuildThermalZoneExtension( AcpiObject, RootDeviceExtension, &deviceExtension ); if (NT_SUCCESS(status)) { // // Incremement the reference count on the node. We do this because // we are going to be doing work (which will take a long time // to complete, anyways), and we don't want to hold the lock for that // entire time. If we incr the reference count, then we guarantee that // no one can come along and kick the feet out from underneath us // InterlockedIncrement( &(deviceExtension->ReferenceCount) ); } // // What happend to the creation of the extension? // if (!NT_SUCCESS(status)) { // // We should have succeeded at whatever we are doing --- so this is // a bad place to be // ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreateThermalZone: NSObj %p Failed %08lx\n", AcpiObject, status ) ); goto OSNotifyCreateThermalZoneExit; } // // Set the optional flags if there are any // ACPIInternalUpdateFlags( &(deviceExtension->Flags), OptionalFlags, FALSE ); // // Make sure to queue the request // status = ACPIBuildThermalZoneRequest( deviceExtension, NULL, NULL, FALSE ); if (!NT_SUCCESS(status)) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyCreateThermalZone: " "ACPIBuildThermalZoneRequest(%p) = %08lx\n", deviceExtension, status ) ); goto OSNotifyCreateThermalZoneExit; } OSNotifyCreateThermalZoneExit: // // There is some work that will be done later // return status; } NTSTATUS EXPORT OSNotifyDeviceCheck( IN PNSOBJ AcpiObject ) /*++ Routine Description: This routine is called when the AML Interpreter signals that the System should check the presence of a device. If the device remains present, nothing is done. If the device appears or disappears the appropriate action is taken. For legacy reasons, if the device is a dock we initiate an undock request. Newer ACPI BIOS's should use Notify(,3). Arguments: AcpiObject - The device we should check for new/missing children. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension; ASSERT( AcpiObject != NULL ); // // Let the world know // ACPIPrint( ( ACPI_PRINT_PNP, "OSNotifyDeviceCheck: 0x%p (%s)\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); deviceExtension = (PDEVICE_EXTENSION) AcpiObject->Context; if (deviceExtension == NULL) { return STATUS_SUCCESS; } // // Notify(,1) on a dock node is an eject request request. Handle specially. // if (ACPIDockIsDockDevice(AcpiObject)) { // // We only let BIOS's get away with this because we rev'd the spec // after Win98. Both OS's will agree with the release of NT5 and // Win98 SP1 // ACPIPrint( ( ACPI_PRINT_WARNING, "OSNotifyDeviceCheck: BIOS issued Notify(dock,1), should use " " Notify(dock,3) to request ejection of a dock.\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); return OSNotifyDeviceEject(AcpiObject) ; } // // Search for the parent of the first device that the OS is aware, and // issue a device check notify // // N.B. // There is currently no way in WDM to do a "light" device check. Once // this is amended, the following code should be updated to do something // more efficient. // deviceExtension = deviceExtension->ParentExtension; while (deviceExtension) { if (!(deviceExtension->Flags & DEV_TYPE_NOT_FOUND)) { // // Invalid the device relations for this device tree // IoInvalidateDeviceRelations( deviceExtension->PhysicalDeviceObject, BusRelations ); break; } // // Try the parent device // deviceExtension = deviceExtension->ParentExtension; } // // Done // return STATUS_SUCCESS; } NTSTATUS EXPORT OSNotifyDeviceEnum( IN PNSOBJ AcpiObject ) /*++ Routine Description: This routine is called when the AML Interpreter signals that the System should re-enumerate the device Arguments: AcpiObject - The device we should check for new/missing children. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension; PDEVICE_EXTENSION dockExtension; ASSERT( AcpiObject != NULL ); // // Let the world know // ACPIPrint( ( ACPI_PRINT_PNP, "OSNotifyDeviceEnum: 0x%p (%s)\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); deviceExtension = (PDEVICE_EXTENSION) AcpiObject->Context; if (deviceExtension == NULL) { return STATUS_SUCCESS; } // // Notify(,0) on a dock node is a dock request. Handle specially. // if (ACPIDockIsDockDevice(AcpiObject)) { dockExtension = ACPIDockFindCorrespondingDock( deviceExtension ); if (!dockExtension) { ACPIPrint( ( ACPI_PRINT_FAILURE, "OSNotifyDeviceEnum: Dock device 0x%p (%s) " "does not have a profile provider!\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); return STATUS_SUCCESS; } // // If this node is marked "Unknown", move it to "Isolated" as // Notify(Dock,0) was ran. If we never saw Notify(Dock,0) but the // dock's _STA said "here", we would assume _DCK(0) was ran by the BIOS // itself. // InterlockedCompareExchange( (PULONG) &dockExtension->Dock.IsolationState, IS_ISOLATED, IS_UNKNOWN ); if (dockExtension->Dock.IsolationState == IS_ISOLATED) { if (dockExtension->Flags&DEV_TYPE_NOT_FOUND) { // // We haven't made a PDO for the docking station yet. This may // be a request to bring it online. Mark the profile provider // so that we notice the new dock appearing // ACPIInternalUpdateFlags( &dockExtension->Flags, DEV_CAP_UNATTACHED_DOCK, FALSE ); } // // Invalidate the beginning of the tree. This will cause our fake // dock node to start. // IoInvalidateDeviceRelations( RootDeviceExtension->PhysicalDeviceObject, SingleBusRelations ); } return STATUS_SUCCESS; } // // Search for the parent of the first device that the OS is aware, and // issue a device check notify // while (deviceExtension) { if (!(deviceExtension->Flags & DEV_TYPE_NOT_FOUND)) { // // Invalid the device relations for this device tree // IoInvalidateDeviceRelations( deviceExtension->PhysicalDeviceObject, BusRelations ); break; } // // Try the parent device // deviceExtension = deviceExtension->ParentExtension; } // // Done // return STATUS_SUCCESS; } NTSTATUS EXPORT OSNotifyDeviceEject( IN PNSOBJ AcpiObject ) /*++ Routine Description: This routine is called when the device's eject button is pressed Arguments: AcpiObject - The device to be ejected Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension; ASSERT( AcpiObject != NULL ); // // Let the world know // ACPIPrint( ( ACPI_PRINT_REMOVE, "OSNotifyDeviceEject: 0x%p (%s)\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); // // Inform the OS of which device wants to go away. If the OS doesn't // know about the device, then don't bother // deviceExtension = (PDEVICE_EXTENSION) AcpiObject->Context; // // If this is a dock, queue the eject against the profile provider. // if (ACPIDockIsDockDevice(AcpiObject)) { deviceExtension = ACPIDockFindCorrespondingDock( deviceExtension ); if (!deviceExtension) { ACPIPrint( ( ACPI_PRINT_FAILURE, "OSNotifyDeviceEject: Dock device 0x%p (%s) " "does not have a profile provider!\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); return STATUS_SUCCESS; } } if (deviceExtension && !(deviceExtension->Flags & DEV_TYPE_NOT_FOUND)) { IoRequestDeviceEject (deviceExtension->PhysicalDeviceObject); } // // Done // return STATUS_SUCCESS; } NTSTATUS EXPORT OSNotifyDeviceWake( IN PNSOBJ AcpiObject ) /*++ Routine Description: This is called when a device has woken the computer Arguments: AcpiObject - The device which woke the computer Return Value: NTSTATUS --*/ { KIRQL oldIrql; NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PLIST_ENTRY powerList; ASSERT( AcpiObject != NULL ); // // Grab the device extension associated with this NS object // deviceExtension = (PDEVICE_EXTENSION) AcpiObject->Context; ASSERT( deviceExtension != NULL ); // // Let the world know // ACPIDevPrint( ( ACPI_PRINT_WAKE, deviceExtension, "OSNotifyDeviceWake - 0x%p (%s)\n", AcpiObject, ACPIAmliNameObject( AcpiObject ) ) ); // // Initialize the list that will hold the requests // powerList = ExAllocatePoolWithTag( NonPagedPool, sizeof(LIST_ENTRY), ACPI_MISC_POOLTAG ); if (powerList == NULL) { ACPIDevPrint( ( ACPI_PRINT_CRITICAL, deviceExtension, "OSNotifyDeviceWake - Cannot Allocate LIST_ENTRY\n" ) ); return STATUS_SUCCESS; } InitializeListHead( powerList ); // // Remove the affected requests from the wait list // IoAcquireCancelSpinLock( &oldIrql ); KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock ); ACPIWakeRemoveDevicesAndUpdate( deviceExtension, powerList ); KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock ); IoReleaseCancelSpinLock( oldIrql ); // // If the list is non-empty, then disable those requests // if (!IsListEmpty( powerList ) ) { status = ACPIWakeDisableAsync( deviceExtension, powerList, OSNotifyDeviceWakeCallBack, powerList ); if (status != STATUS_PENDING) { OSNotifyDeviceWakeCallBack( NULL, status, NULL, powerList ); } ACPIDevPrint( ( ACPI_PRINT_WAKE, deviceExtension, "OSNotifyDeviceWake - ACPIWakeDisableAsync = %08lx\n", status ) ); } else { // // We must free this memory ourselves // ExFreePool( powerList ); } // // Done // return STATUS_SUCCESS; } VOID EXPORT OSNotifyDeviceWakeCallBack( IN PNSOBJ AcpiObject, IN NTSTATUS Status, IN POBJDATA ObjectData, IN PVOID Context ) /*++ Routine Description: This routine is called when we have completed _PSW(off) on a device Arguments: AcpiObject - Points to the control method that was run Status - Result of the method ObjectData - Information about the result Context - P{DEVICE_EXTENSION Return Value: NTSTATUS --*/ { #if DBG PACPI_POWER_REQUEST powerRequest; PDEVICE_EXTENSION deviceExtension; #endif PLIST_ENTRY powerList = (PLIST_ENTRY) Context; // // Do we have some work to do? // if (IsListEmpty( powerList ) ) { ACPIPrint( ( ACPI_PRINT_WARNING, "OSNotifyDeviceWakeCallBack: %p is an empty list\n", powerList ) ); ExFreePool( powerList ); return; } #if DBG // // Get the first record, so that we have a clue as to the device // that was completed // powerRequest = CONTAINING_RECORD( powerList->Flink, ACPI_POWER_REQUEST, ListEntry ); ASSERT( powerRequest->Signature == ACPI_SIGNATURE ); // // Grab the device extension // deviceExtension = powerRequest->DeviceExtension; // // Tell the world // ACPIDevPrint( ( ACPI_PRINT_WAKE, deviceExtension, "OSNotifyDeviceWakeCallBack = 0x%08lx\n", Status ) ); #endif // // Complete the requests // ACPIWakeCompleteRequestQueue( powerList, Status ); // // Free the list pointer // ExFreePool( powerList ); } VOID EXPORT OSNotifyDeviceWakeByGPEEvent( IN ULONG GpeIndex, IN ULONG GpeRegister, IN ULONG GpeMask ) /*++ Routine Description: This is called when a device has woken the computer Arguments: GpeIndex - The index bit of the GPE that woke the computer GpeRegister - The register index GpeMask - The enabled bits for that register Return Value: NTSTATUS --*/ { KIRQL oldIrql; NTSTATUS status; PACPI_POWER_REQUEST powerRequest; PDEVICE_EXTENSION deviceExtension; PLIST_ENTRY listEntry; PLIST_ENTRY powerList; // // Let the world know // ACPIPrint( ( ACPI_PRINT_WAKE, "OSNotifyDeviceWakeByGPEEvent: %02lx[%x] & %02lx\n", GpeRegister, GpeIndex, GpeMask ) ); // // Initialize the list that will hold the requests // powerList = ExAllocatePoolWithTag( NonPagedPool, sizeof(LIST_ENTRY), ACPI_MISC_POOLTAG ); if (powerList == NULL) { ACPIPrint( ( ACPI_PRINT_CRITICAL, "OSNotifyDeviceWakeByGPEEvent: Cannot Allocate LIST_ENTRY\n" ) ); return; } InitializeListHead( powerList ); // // We need to be holding these locks // IoAcquireCancelSpinLock( &oldIrql ); KeAcquireSpinLockAtDpcLevel( &AcpiPowerLock ); // // Look for a matching power request for this GPE // for (listEntry = AcpiPowerWaitWakeList.Flink; listEntry != &AcpiPowerWaitWakeList; listEntry = listEntry->Flink) { // // Grab the request // powerRequest = CONTAINING_RECORD( listEntry, ACPI_POWER_REQUEST, ListEntry ); ASSERT( powerRequest->Signature == ACPI_SIGNATURE ); deviceExtension = powerRequest->DeviceExtension; // // See if this request matches // if (deviceExtension->PowerInfo.WakeBit == GpeIndex) { // // Get all of the wait requests for this device // ACPIWakeRemoveDevicesAndUpdate( deviceExtension, powerList ); break; } } // // This is an exclusive wake gpe bit --- verify there are not multiple // devices waiting for it, as that would be a design which could cause a // deadlock // if (!IsListEmpty( powerList ) ) { ASSERT( !(GpeWakeEnable[GpeRegister] & GpeMask) ); } // // No longer need these locks // KeReleaseSpinLockFromDpcLevel( &AcpiPowerLock ); IoReleaseCancelSpinLock( oldIrql ); // // If the list is non-empty, then disable those requests // if (!IsListEmpty( powerList ) ) { status = ACPIWakeDisableAsync( deviceExtension, powerList, OSNotifyDeviceWakeCallBack, powerList ); if (status != STATUS_PENDING) { OSNotifyDeviceWakeCallBack( NULL, status, NULL, powerList ); } ACPIDevPrint( ( ACPI_PRINT_WAKE, deviceExtension, "OSNotifyDeviceWakeByGPEIndex - ACPIWakeDisableAsync = %08lx\n", status ) ); } else { // // We must free this memory ourselves // ExFreePool( powerList ); } // // Done // return; } NTSTATUS EXPORT OSNotifyFatalError( IN ULONG Param1, IN ULONG Param2, IN ULONG Param3, IN ULONG_PTR AmlContext, IN ULONG_PTR Context ) /*++ Routine Description: This routine is called whenever the AML code detects a condition that the machine can no longer handle. It --*/ { KIRQL oldIrql; // // Acquire the spinlock and see if there is an outstanding fatal error // pending already // KeAcquireSpinLock( &AcpiPowerLock, &oldIrql ); if (AcpiFatalOutstanding != FALSE) { // // There is one outstanding already... don't do anything // KeReleaseSpinLock( &AcpiPowerLock, oldIrql ); return STATUS_SUCCESS; } // // Remember that there is an outstanding fatal context and release the lock AcpiFatalOutstanding = TRUE; KeReleaseSpinLock(&AcpiPowerLock, oldIrql); // // Initialize the work queue // ExInitializeWorkItem( &(AcpiFatalContext.Item), OSNotifyFatalErrorWorker, &AcpiFatalContext ); AcpiFatalContext.Param1 = Param1; AcpiFatalContext.Param2 = Param2; AcpiFatalContext.Param3 = Param3; AcpiFatalContext.Context = AmlContext; // // Queue the work item and return // ExQueueWorkItem( &(AcpiFatalContext.Item), DelayedWorkQueue ); return STATUS_SUCCESS; } VOID OSNotifyFatalErrorWorker( IN PVOID Context ) /*++ Routine Description: This is the routine that actually shuts down the machine on a fatal error Arguments: Context - Points to the fatal error context Return Value: None --*/ { PACPI_FATAL_ERROR_CONTEXT fatal = (PACPI_FATAL_ERROR_CONTEXT) Context; #if 0 PWCHAR stringData[1]; ULONG data[3]; // // Generate the parameters for an error log message // stringData[0] = L"Acpi"; data[0] = fatal->Param1; data[1] = fatal->Param2; data[2] = fatal->Param3; // // Write the error log message // ACPIErrLogWriteEventLogEntry( ACPI_ERR_BIOS_FATAL, 0, 1, &stringData, sizeof(ULONG) * 3, data ); #else // // Now, we can bugcheck // PoShutdownBugCheck( TRUE, ACPI_BIOS_FATAL_ERROR, fatal->Param1, fatal->Param2, fatal->Param3, fatal->Context ); #endif }