1809 lines
44 KiB
C
1809 lines
44 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
thermal.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Thermal Zone support
|
|||
|
|
|||
|
A small discourse on the use and function of the THRM_WAIT_FOR_NOTIFY
|
|||
|
flag. This flag was added to ensure that at least one Notify() operation
|
|||
|
occured between each query of the temperature. In other words, we didn't
|
|||
|
want to loop forever asking and receiving the same temperature information.
|
|||
|
|
|||
|
One of the side effects of this flag is that if we get a QUERY, then
|
|||
|
a SET (instead of another QUERY), then the set must clear the flag.
|
|||
|
Failure to do so will prevent the ThermalLoop() code from ever completing
|
|||
|
the IRP. And that means that the temperature mechanisms will stop working.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Stephane Plante (splante)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
NT Kernel Model Driver only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
July 7, 1997 - Complete Rewrite
|
|||
|
--*/
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
|
|||
|
WMIGUIDREGINFO ACPIThermalGuidList =
|
|||
|
{
|
|||
|
&THERMAL_ZONE_GUID,
|
|||
|
1,
|
|||
|
0
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Spinlock to protect the thermal list
|
|||
|
//
|
|||
|
KSPIN_LOCK AcpiThermalLock;
|
|||
|
|
|||
|
//
|
|||
|
// List entry to store the thermal requests on
|
|||
|
//
|
|||
|
LIST_ENTRY AcpiThermalList;
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, ACPIThermalStartDevice)
|
|||
|
#pragma alloc_text(PAGE, ACPIThermalWorker)
|
|||
|
#pragma alloc_text(PAGE, ACPIThermalQueryWmiRegInfo)
|
|||
|
#pragma alloc_text(PAGE, ACPIThermalQueryWmiDataBlock)
|
|||
|
#pragma alloc_text(PAGE, ACPIThermalWmi)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
ACPIThermalCalculateProcessorMask(
|
|||
|
IN PNSOBJ ProcessorObject,
|
|||
|
IN PTHRM_INFO Thrm
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine, which is only called from ACPIThermalWorker, has been
|
|||
|
created so that we don't have to worry about locking down the
|
|||
|
ACPIThermalWorker, takes a processor object from the namespace and
|
|||
|
sets the proper affinity bit in the thermal info
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ProcessorObject - Pointer to Namespace Processor Object
|
|||
|
Thrm - Thermal Information Structure
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
KIRQL OldIrql;
|
|||
|
PDEVICE_EXTENSION ProcessorExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Sanity check
|
|||
|
//
|
|||
|
if (ProcessorObject == NULL) {
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We need the spinlock to deref the processor extension
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &OldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// The context pointer is the device extension
|
|||
|
//
|
|||
|
ProcessorExtension = (PDEVICE_EXTENSION) ProcessorObject->Context;
|
|||
|
if (ProcessorExtension) {
|
|||
|
|
|||
|
//
|
|||
|
// We know what index it is within the processor list.
|
|||
|
// This should be a good enough guess for now
|
|||
|
//
|
|||
|
Thrm->Info.Processors |= (1 << ProcessorExtension->Processor.ProcessorIndex);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done with the spinlock
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, OldIrql );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPIThermalCancelRequest (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine cancels an outstanding thermal request
|
|||
|
|
|||
|
Arguments
|
|||
|
|
|||
|
DeviceObject - The device which has a request being cancelled
|
|||
|
Irp - The cancelling irp
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
KIRQL oldIrql;
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
|
|||
|
#if DBG
|
|||
|
ULONGLONG currentTime = KeQueryInterruptTime();
|
|||
|
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalCancelRequest: Irp %08lx\n",
|
|||
|
Irp
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// We no longer need the cancel lock
|
|||
|
//
|
|||
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|||
|
|
|||
|
//
|
|||
|
// We do however need the thermal queue lock
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiThermalLock, &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Remove the irp from the list that it is on
|
|||
|
//
|
|||
|
RemoveEntryList( &(Irp->Tail.Overlay.ListEntry) );
|
|||
|
|
|||
|
//
|
|||
|
// Done with the thermal lock now
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiThermalLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Complete the irp now
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|||
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
EXPORT
|
|||
|
ACPIThermalComplete (
|
|||
|
IN PNSOBJ AcpiObject,
|
|||
|
IN NTSTATUS Status,
|
|||
|
IN POBJDATA Result OPTIONAL,
|
|||
|
IN PVOID DeviceExtension
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when the interpreter has completed a request
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AcpiObject - The request that was completed
|
|||
|
Status - The status of the request
|
|||
|
Result - What the result of the request was
|
|||
|
DevExt - The context of the request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ACPIThermalLoop (DeviceExtension, THRM_BUSY);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ACPIThermalCompletePendingIrps(
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PTHRM_INFO Thermal
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called, with no spinlock being held. This routine
|
|||
|
completes any IOCTLs associated with the device object
|
|||
|
|
|||
|
This routine will return TRUE if it completed any requests, false
|
|||
|
otherwise
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - The device extension whose requests we want to complete
|
|||
|
Thermal - Pointer to the thermal information for the extension
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN handledRequest = FALSE;
|
|||
|
KIRQL oldIrql;
|
|||
|
LIST_ENTRY doneList;
|
|||
|
PDEVICE_EXTENSION irpExtension;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PIRP irp;
|
|||
|
PLIST_ENTRY listEntry;
|
|||
|
PTHERMAL_INFORMATION thermalInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the list that will hold the requets that we need to complete
|
|||
|
//
|
|||
|
InitializeListHead( &doneList );
|
|||
|
//
|
|||
|
// Acquire the thermal lock so that we can pend these requests
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiThermalLock, &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Walk the list of pending irps to see which ones match this extensions
|
|||
|
//
|
|||
|
listEntry = AcpiThermalList.Flink;
|
|||
|
while (listEntry != &AcpiThermalList) {
|
|||
|
|
|||
|
//
|
|||
|
// Grab the irp from the list entry and update the next list entry
|
|||
|
// that we will look at
|
|||
|
//
|
|||
|
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
|||
|
listEntry = listEntry->Flink;
|
|||
|
|
|||
|
//
|
|||
|
// We need the current irp stack location
|
|||
|
//
|
|||
|
irpSp = IoGetCurrentIrpStackLocation( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Grab the device object from the irp stack and turn that into a
|
|||
|
// device extension
|
|||
|
//
|
|||
|
irpExtension = ACPIInternalGetDeviceExtension( irpSp->DeviceObject );
|
|||
|
|
|||
|
//
|
|||
|
// Is this an irp that we care about? IE: does the target match the
|
|||
|
// one specified in this function
|
|||
|
//
|
|||
|
if (irpExtension != DeviceExtension) {
|
|||
|
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a query information irp then, we must be able to set the
|
|||
|
// cancel routine to NULL to make sure that it cannot be cancelled on
|
|||
|
// us
|
|||
|
//
|
|||
|
if (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_THERMAL_QUERY_INFORMATION) {
|
|||
|
|
|||
|
if (IoSetCancelRoutine(irp, NULL) == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Cancel routine is active, stop processing this irp and move on
|
|||
|
//
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the data that we got back to the irp
|
|||
|
//
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_WAIT_FOR_NOTIFY;
|
|||
|
thermalInfo = (PTHERMAL_INFORMATION) irp->AssociatedIrp.SystemBuffer;
|
|||
|
memcpy (thermalInfo, Thermal, sizeof (THERMAL_INFORMATION));
|
|||
|
|
|||
|
//
|
|||
|
// Set the parameters that we will return
|
|||
|
//
|
|||
|
irp->IoStatus.Information = sizeof(THERMAL_INFORMATION);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Set the parameters that we will return
|
|||
|
//
|
|||
|
irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Always succeed these irps
|
|||
|
//
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Remove the entry from the list
|
|||
|
//
|
|||
|
RemoveEntryList( &(irp->Tail.Overlay.ListEntry) );
|
|||
|
|
|||
|
//
|
|||
|
// Insert the list into the next queue so that we know to complete it
|
|||
|
// later on
|
|||
|
//
|
|||
|
InsertTailList( &doneList, &(irp->Tail.Overlay.ListEntry) );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, drop our thermal lock
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiThermalLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Walk the list of irpts to be completed
|
|||
|
//
|
|||
|
listEntry = doneList.Flink;
|
|||
|
while (listEntry != &doneList) {
|
|||
|
|
|||
|
//
|
|||
|
// Grab the irp from the list entry and update the next list entry
|
|||
|
// that we will look at
|
|||
|
//
|
|||
|
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
|||
|
listEntry = listEntry->Flink;
|
|||
|
RemoveEntryList( &(irp->Tail.Overlay.ListEntry) );
|
|||
|
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
KeQueryInterruptTime(),
|
|||
|
"Completing Irp 0x%x\n",
|
|||
|
irp
|
|||
|
) );
|
|||
|
|
|||
|
//
|
|||
|
// Now complete the irp
|
|||
|
//
|
|||
|
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
|||
|
|
|||
|
//
|
|||
|
// Remember that we handled a request
|
|||
|
//
|
|||
|
handledRequest = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return wether or not we handled a request
|
|||
|
return handledRequest;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIThermalDeviceControl (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Fixed button device IOCTL handler
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - fixed feature button device object
|
|||
|
Irp - the ioctl request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
KIRQL oldIrql;
|
|||
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
PTHERMAL_INFORMATION thermalInfo;
|
|||
|
PULONG Mode;
|
|||
|
PTHRM_INFO Thrm = deviceExtension->Thermal.Info;
|
|||
|
NTSTATUS Status = STATUS_PENDING;
|
|||
|
ULONG ThermalWork = 0;
|
|||
|
ULONGLONG currentTime;
|
|||
|
|
|||
|
//
|
|||
|
// Do not allow user mode IRPs in this routine
|
|||
|
//
|
|||
|
if (Irp->RequestorMode != KernelMode) {
|
|||
|
|
|||
|
return ACPIDispatchIrpInvalid( DeviceObject, Irp );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
currentTime = KeQueryInterruptTime();
|
|||
|
#endif
|
|||
|
|
|||
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
case IOCTL_THERMAL_QUERY_INFORMATION:
|
|||
|
|
|||
|
//
|
|||
|
// If this irp's stamp does not match the known last stamp, then we
|
|||
|
// need a new temp now
|
|||
|
//
|
|||
|
thermalInfo = (PTHERMAL_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|||
|
if (thermalInfo->ThermalStamp != Thrm->Info.ThermalStamp) {
|
|||
|
|
|||
|
ThermalWork = THRM_TEMP | THRM_WAIT_FOR_NOTIFY;
|
|||
|
|
|||
|
}
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
currentTime,
|
|||
|
"%08x - THERMAL_QUERY_INFORMATION: %x - %x\n",
|
|||
|
Irp,
|
|||
|
thermalInfo->ThermalStamp,
|
|||
|
Thrm->Info.ThermalStamp
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_THERMAL_SET_COOLING_POLICY:
|
|||
|
|
|||
|
//
|
|||
|
// Set the thermal zone's policy mode
|
|||
|
//
|
|||
|
Thrm->Mode = *((PUCHAR) Irp->AssociatedIrp.SystemBuffer);
|
|||
|
ThermalWork = THRM_MODE | THRM_TRIP_POINTS | THRM_WAIT_FOR_NOTIFY;
|
|||
|
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
currentTime,
|
|||
|
"%08x - SET_COOLING_POLICY: %x\n",
|
|||
|
Irp,
|
|||
|
Thrm->Mode
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_RUN_ACTIVE_COOLING_METHOD:
|
|||
|
|
|||
|
Thrm->CoolingLevel = *((PUCHAR) Irp->AssociatedIrp.SystemBuffer);
|
|||
|
ThermalWork = THRM_COOLING_LEVEL | THRM_WAIT_FOR_NOTIFY;
|
|||
|
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
currentTime,
|
|||
|
"%08x - ACTIVE_COOLING_LEVEL: %x\n",
|
|||
|
Irp,
|
|||
|
Thrm->CoolingLevel
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|||
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|||
|
return STATUS_NOT_SUPPORTED;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Grab the thermal lock, queue the request to the proper place, and make
|
|||
|
// sure to set a cancel routine --- no that we will only allow a cancel
|
|||
|
// routine if this is a query irp
|
|||
|
//
|
|||
|
KeAcquireSpinLock( &AcpiThermalLock, &oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// There is one fly in the ointment: What to do if the device is no longer
|
|||
|
// there. The only way to really handle that is to just fail the request.
|
|||
|
// Its important to note that this check is done while the ThermalLock
|
|||
|
// is being held because the code that builds a SurpriseRemoved extension
|
|||
|
// will attempt to call AcpiThermalCompletePendingIrps which also
|
|||
|
// acquires this lock.
|
|||
|
//
|
|||
|
if (deviceExtension->Flags & DEV_TYPE_SURPRISE_REMOVED) {
|
|||
|
|
|||
|
KeReleaseSpinLock( &AcpiThermalLock, oldIrql );
|
|||
|
Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_THERMAL_QUERY_INFORMATION) {
|
|||
|
|
|||
|
IoSetCancelRoutine (Irp, ACPIThermalCancelRequest);
|
|||
|
if (Irp->Cancel && IoSetCancelRoutine( Irp, NULL ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we got here, that means that the irp has been cancelled and we
|
|||
|
// beat the IO manager to the ThermalLock. So release the irp, and
|
|||
|
// mark the irp as being cancelled
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiThermalLock, oldIrql );
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|||
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|||
|
return STATUS_CANCELLED;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we got to this point, we are going to queue the request and do some
|
|||
|
// work on it later
|
|||
|
//
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we got here, we know we can queue the irp in the outstanding
|
|||
|
// work list entry
|
|||
|
//
|
|||
|
InsertTailList( &AcpiThermalList, &(Irp->Tail.Overlay.ListEntry) );
|
|||
|
|
|||
|
//
|
|||
|
// Done with the lock at this point
|
|||
|
//
|
|||
|
KeReleaseSpinLock( &AcpiThermalLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Fire off the workter thread
|
|||
|
//
|
|||
|
ACPIThermalLoop (deviceExtension, ThermalWork);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPIThermalEvent (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN ULONG EventData
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles thermal events
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device that received the event
|
|||
|
EventData - The event that just happened
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NONE
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
ULONG clear;
|
|||
|
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
KeQueryInterruptTime(),
|
|||
|
"ACPIThermalEvent - Notify(%x)\n",
|
|||
|
EventData
|
|||
|
) );
|
|||
|
|
|||
|
//
|
|||
|
// Handle event type
|
|||
|
//
|
|||
|
clear = 0;
|
|||
|
switch (EventData) {
|
|||
|
case 0x80:
|
|||
|
|
|||
|
//
|
|||
|
// Tempature changed notification
|
|||
|
//
|
|||
|
clear = THRM_WAIT_FOR_NOTIFY | THRM_TEMP;
|
|||
|
break;
|
|||
|
|
|||
|
case 0x81:
|
|||
|
|
|||
|
//
|
|||
|
// Trips points changed notification
|
|||
|
//
|
|||
|
clear = THRM_WAIT_FOR_NOTIFY | THRM_TEMP | THRM_TRIP_POINTS;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
ACPIThermalLoop (deviceExtension, clear);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIThermalFanStartDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine that processes the start device for the fans
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The fan device
|
|||
|
Irp - The start request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
UCHAR minorFunction = irpStack->MinorFunction;
|
|||
|
|
|||
|
//
|
|||
|
// There is nothing to do when starting a fan --- it is really being
|
|||
|
// controlled by the thermal zones
|
|||
|
//
|
|||
|
deviceExtension->DeviceState = Started;
|
|||
|
|
|||
|
//
|
|||
|
// Complete the request
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = (ULONG_PTR) NULL;
|
|||
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|||
|
|
|||
|
//
|
|||
|
// Let the world know
|
|||
|
//
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
"(0x%08lx): %s = 0x%08lx\n",
|
|||
|
Irp,
|
|||
|
ACPIDebugGetIrpText(IRP_MJ_PNP, minorFunction),
|
|||
|
STATUS_SUCCESS
|
|||
|
) );
|
|||
|
|
|||
|
//
|
|||
|
// Done
|
|||
|
//
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPIThermalLoop (
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN ULONG Clear
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine that processes all thermal events
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DevExt - The device extension of the thermal zone
|
|||
|
Clear - Bits to clear
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN doneRequests;
|
|||
|
BOOLEAN lockHeld;
|
|||
|
KIRQL oldIrql;
|
|||
|
PTHRM_INFO thermal;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
thermal = DeviceExtension->Thermal.Info;
|
|||
|
|
|||
|
KeAcquireSpinLock (&DeviceExtension->Thermal.SpinLock, &oldIrql);
|
|||
|
lockHeld = TRUE;
|
|||
|
|
|||
|
DeviceExtension->Thermal.Flags &= ~Clear;
|
|||
|
|
|||
|
//
|
|||
|
// If we're not in the service loop, enter it now
|
|||
|
//
|
|||
|
if (!(DeviceExtension->Thermal.Flags & THRM_IN_SERVICE_LOOP)) {
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_IN_SERVICE_LOOP;
|
|||
|
|
|||
|
//
|
|||
|
// Loop while there's work to be done
|
|||
|
//
|
|||
|
for (; ;) {
|
|||
|
|
|||
|
//
|
|||
|
// Synchronize the thermal zone
|
|||
|
//
|
|||
|
if (!lockHeld) {
|
|||
|
|
|||
|
KeAcquireSpinLock(&DeviceExtension->Thermal.SpinLock, &oldIrql);
|
|||
|
lockHeld = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If some work is pending, wait for it to complete
|
|||
|
//
|
|||
|
if (DeviceExtension->Thermal.Flags & THRM_BUSY) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the thermal zone is initialized. This must
|
|||
|
// be the first thing that we do in the loop!!!
|
|||
|
//
|
|||
|
if (!(DeviceExtension->Thermal.Flags & THRM_INITIALIZE) ) {
|
|||
|
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_BUSY | THRM_INITIALIZE;
|
|||
|
ACPISetDeviceWorker(
|
|||
|
DeviceExtension,
|
|||
|
THRM_COOLING_LEVEL | THRM_INITIALIZE
|
|||
|
);
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the thermal zone mode needs updated, do it now
|
|||
|
//
|
|||
|
if (!(DeviceExtension->Thermal.Flags & THRM_MODE)) {
|
|||
|
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_BUSY | THRM_MODE;
|
|||
|
KeReleaseSpinLock (&DeviceExtension->Thermal.SpinLock, oldIrql);
|
|||
|
lockHeld = FALSE;
|
|||
|
|
|||
|
status = ACPIGetNothingEvalIntegerAsync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_SCP,
|
|||
|
thermal->Mode,
|
|||
|
ACPIThermalComplete,
|
|||
|
DeviceExtension
|
|||
|
);
|
|||
|
if (status != STATUS_PENDING) {
|
|||
|
|
|||
|
ACPIThermalComplete(
|
|||
|
NULL,
|
|||
|
status,
|
|||
|
NULL,
|
|||
|
DeviceExtension
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the trip point infomation needs updated, get it. Note that
|
|||
|
// updating the trip points means that we also need to redo the
|
|||
|
// cooling level
|
|||
|
//
|
|||
|
if (!(DeviceExtension->Thermal.Flags & THRM_TRIP_POINTS)) {
|
|||
|
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_BUSY | THRM_TRIP_POINTS;
|
|||
|
ACPISetDeviceWorker( DeviceExtension, THRM_TRIP_POINTS );
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the cooling level has changed,
|
|||
|
//
|
|||
|
if (!(DeviceExtension->Thermal.Flags & THRM_COOLING_LEVEL)) {
|
|||
|
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_BUSY | THRM_COOLING_LEVEL;
|
|||
|
ACPISetDeviceWorker (DeviceExtension, THRM_COOLING_LEVEL);
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Prevent the recursion that occurs when we complete an irp and
|
|||
|
// the completion routine is able to queue the IRP before we resume
|
|||
|
// the loop
|
|||
|
//
|
|||
|
if ( (DeviceExtension->Thermal.Flags & THRM_WAIT_FOR_NOTIFY) &&
|
|||
|
(DeviceExtension->Thermal.Flags & THRM_TEMP) ) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we don't have a temp, get it
|
|||
|
//
|
|||
|
if (!(DeviceExtension->Thermal.Flags & THRM_TEMP)) {
|
|||
|
|
|||
|
//
|
|||
|
// Is the temp object not present?
|
|||
|
//
|
|||
|
#if DBG
|
|||
|
if (thermal->TempMethod == NULL) {
|
|||
|
|
|||
|
ACPIInternalError( ACPI_THERMAL );
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
thermal->Info.ThermalStamp += 1;
|
|||
|
DeviceExtension->Thermal.Flags |= THRM_BUSY | THRM_TEMP;
|
|||
|
KeReleaseSpinLock (&DeviceExtension->Thermal.SpinLock, oldIrql);
|
|||
|
lockHeld = FALSE;
|
|||
|
|
|||
|
RtlZeroMemory (&thermal->Temp, sizeof(OBJDATA));
|
|||
|
|
|||
|
thermal->Temp.dwDataType = OBJTYPE_UNKNOWN;
|
|||
|
status = AMLIAsyncEvalObject(
|
|||
|
thermal->TempMethod,
|
|||
|
&thermal->Temp,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
ACPIThermalTempatureRead,
|
|||
|
DeviceExtension
|
|||
|
);
|
|||
|
|
|||
|
if (status != STATUS_PENDING) {
|
|||
|
|
|||
|
ACPIThermalTempatureRead(
|
|||
|
thermal->TempMethod,
|
|||
|
status,
|
|||
|
&thermal->Temp,
|
|||
|
DeviceExtension
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Everything is up to date. Check for a pending irp to see if
|
|||
|
// we can complete it.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Call into a child function to determine if we have completed
|
|||
|
// any requests
|
|||
|
//
|
|||
|
doneRequests = ACPIThermalCompletePendingIrps(
|
|||
|
DeviceExtension,
|
|||
|
thermal
|
|||
|
);
|
|||
|
if (doneRequests) {
|
|||
|
|
|||
|
continue;
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// No longer in the serivce loop
|
|||
|
//
|
|||
|
DeviceExtension->Thermal.Flags &= ~THRM_IN_SERVICE_LOOP;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
KeReleaseSpinLock (&DeviceExtension->Thermal.SpinLock, oldIrql);
|
|||
|
return ;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPIThermalPowerCallback (
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN PVOID Context,
|
|||
|
IN NTSTATUS Status
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine that is called after we have sent an internal
|
|||
|
power request to the device
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - the device that was set
|
|||
|
Context - Not used
|
|||
|
Status - Result
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_CRITICAL,
|
|||
|
DeviceExtension,
|
|||
|
"ACPIThermalPowerCallBack: failed power setting %x\n",
|
|||
|
Status
|
|||
|
) );
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIThermalQueryWmiDataBlock(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN ULONG GuidIndex,
|
|||
|
IN ULONG InstanceIndex,
|
|||
|
IN ULONG InstanceCount,
|
|||
|
IN OUT PULONG InstanceLengthArray,
|
|||
|
IN ULONG BufferAvail,
|
|||
|
OUT PUCHAR Buffer
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is a callback into the driver to query for the contents of
|
|||
|
all instances of a data block. When the driver has finished filling the
|
|||
|
data block it must call WmiCompleteRequest to complete the irp. The
|
|||
|
driver can return STATUS_PENDING if the irp cannot be completed
|
|||
|
immediately.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject is the device whose data block is being queried
|
|||
|
|
|||
|
Irp is the Irp that makes this request
|
|||
|
|
|||
|
GuidIndex is the index into the list of guids provided when the
|
|||
|
device registered
|
|||
|
|
|||
|
InstanceCount is the number of instnaces expected to be returned for
|
|||
|
the data block.
|
|||
|
|
|||
|
InstanceLengthArray is a pointer to an array of ULONG that returns the
|
|||
|
lengths of each instance of the data block. If this is NULL then
|
|||
|
there was not enough space in the output buffer to fufill the request
|
|||
|
so the irp should be completed with the buffer needed.
|
|||
|
|
|||
|
BufferAvail on entry has the maximum size available to write the data
|
|||
|
blocks.
|
|||
|
|
|||
|
Buffer on return is filled with the returned data blocks. Note that each
|
|||
|
instance of the data block must be aligned on a 8 byte boundry.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
ULONG sizeNeeded;
|
|||
|
PTHRM_INFO info;
|
|||
|
PTHERMAL_INFORMATION thermalInfo;
|
|||
|
PTHERMAL_INFORMATION wmiThermalInfo;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
|
|||
|
if (GuidIndex == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// ThermalZone temperature query
|
|||
|
//
|
|||
|
info = (PTHRM_INFO) deviceExtension->Thermal.Info;
|
|||
|
thermalInfo = &info->Info;
|
|||
|
|
|||
|
wmiThermalInfo = (PTHERMAL_INFORMATION)Buffer;
|
|||
|
sizeNeeded = sizeof(THERMAL_INFORMATION);
|
|||
|
|
|||
|
if (BufferAvail >= sizeNeeded) {
|
|||
|
|
|||
|
// NOTE - Synchronize with thread getting this data
|
|||
|
*InstanceLengthArray = sizeNeeded;
|
|||
|
RtlCopyMemory(wmiThermalInfo, thermalInfo, sizeNeeded);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
status = STATUS_WMI_GUID_NOT_FOUND;
|
|||
|
sizeNeeded = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
status = WmiCompleteRequest(
|
|||
|
DeviceObject,
|
|||
|
Irp,
|
|||
|
status,
|
|||
|
sizeNeeded,
|
|||
|
IO_NO_INCREMENT
|
|||
|
);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIThermalQueryWmiRegInfo(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
OUT ULONG *RegFlags,
|
|||
|
OUT PUNICODE_STRING InstanceName,
|
|||
|
OUT PUNICODE_STRING *RegistryPath,
|
|||
|
OUT PUNICODE_STRING MofResourceName,
|
|||
|
OUT PDEVICE_OBJECT *Pdo
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is a callback into the driver to retrieve information about
|
|||
|
the guids being registered.
|
|||
|
|
|||
|
Implementations of this routine may be in paged memory
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject is the device whose registration information is needed
|
|||
|
|
|||
|
*RegFlags returns with a set of flags that describe all of the guids being
|
|||
|
registered for this device. If the device wants enable and disable
|
|||
|
collection callbacks before receiving queries for the registered
|
|||
|
guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
|
|||
|
returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
|
|||
|
the instance name is determined from the PDO associated with the
|
|||
|
device object. Note that the PDO must have an associated devnode. If
|
|||
|
WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
|
|||
|
name for the device. These flags are ORed into the flags specified
|
|||
|
by the GUIDREGINFO for each guid.
|
|||
|
|
|||
|
InstanceName returns with the instance name for the guids if
|
|||
|
WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
|
|||
|
caller will call ExFreePool with the buffer returned.
|
|||
|
|
|||
|
*RegistryPath returns with the registry path of the driver. This is
|
|||
|
required
|
|||
|
|
|||
|
MofResourceName returns with the name of the MOF resource attached to
|
|||
|
the binary file. If the driver does not have a mof resource attached
|
|||
|
then this can be returned unmodified. If a value is returned then
|
|||
|
it is NOT freed.
|
|||
|
|
|||
|
*Pdo returns with the device object for the PDO associated with this
|
|||
|
device if the WMIREG_FLAG_INSTANCE_PDO flag is retured in
|
|||
|
*RegFlags.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (AcpiRegistryPath.Buffer != NULL) {
|
|||
|
|
|||
|
*RegistryPath = &AcpiRegistryPath;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
*RegistryPath = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
*RegFlags = WMIREG_FLAG_INSTANCE_PDO;
|
|||
|
*Pdo = DeviceObject;
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIThermalStartDevice (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to start the thermal zone
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - The device that is starting up
|
|||
|
Irp - The request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
PWMILIB_CONTEXT wmilibContext;
|
|||
|
|
|||
|
deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_IRP,
|
|||
|
deviceExtension,
|
|||
|
"(0x%08lx): IRP_MN_START_DEVICE\n",
|
|||
|
Irp
|
|||
|
) );
|
|||
|
|
|||
|
status = ACPIInternalSetDeviceInterface (
|
|||
|
DeviceObject,
|
|||
|
(LPGUID) &GUID_DEVICE_THERMAL_ZONE
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ACPIDevPrint( (
|
|||
|
ACPI_PRINT_FAILURE,
|
|||
|
deviceExtension,
|
|||
|
"ACPIThermalStartDevice -> SetDeviceInterface = 0x%08lx\n",
|
|||
|
status
|
|||
|
) );
|
|||
|
goto ACPIThermalStartDeviceExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ACPIRegisterForDeviceNotifications(
|
|||
|
DeviceObject,
|
|||
|
(PDEVICE_NOTIFY_CALLBACK) ACPIThermalEvent,
|
|||
|
(PVOID) DeviceObject
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize device object for WMILIB
|
|||
|
//
|
|||
|
wmilibContext = ExAllocatePoolWithTag(
|
|||
|
PagedPool,
|
|||
|
sizeof(WMILIB_CONTEXT),
|
|||
|
ACPI_THERMAL_POOLTAG
|
|||
|
);
|
|||
|
if (wmilibContext == NULL) {
|
|||
|
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto ACPIThermalStartDeviceExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(wmilibContext, sizeof(WMILIB_CONTEXT));
|
|||
|
wmilibContext->GuidCount = ACPIThermalGuidCount;
|
|||
|
wmilibContext->GuidList = &ACPIThermalGuidList;
|
|||
|
wmilibContext->QueryWmiRegInfo = ACPIThermalQueryWmiRegInfo;
|
|||
|
wmilibContext->QueryWmiDataBlock = ACPIThermalQueryWmiDataBlock;
|
|||
|
deviceExtension->Thermal.WmilibContext = wmilibContext;
|
|||
|
|
|||
|
//
|
|||
|
// Register for WMI events
|
|||
|
//
|
|||
|
status = IoWMIRegistrationControl(
|
|||
|
DeviceObject,
|
|||
|
WMIREG_ACTION_REGISTER
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
deviceExtension->Thermal.WmilibContext = NULL;
|
|||
|
ExFreePool(wmilibContext);
|
|||
|
goto ACPIThermalStartDeviceExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Mark the device as started
|
|||
|
//
|
|||
|
deviceExtension->DeviceState = Started;
|
|||
|
|
|||
|
//
|
|||
|
// Request that the device go to the D0 state
|
|||
|
// Note: that we don't block on this call, since we assume that
|
|||
|
// we can process thermal events asynchronously from being in
|
|||
|
// the D0 state. However, there may be a future occasion where
|
|||
|
// this is not true, so this makes the code more ready to handle
|
|||
|
// that case
|
|||
|
//
|
|||
|
status = ACPIDeviceInternalDeviceRequest(
|
|||
|
deviceExtension,
|
|||
|
PowerDeviceD0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Start the thermal engine
|
|||
|
//
|
|||
|
ACPIThermalLoop( deviceExtension, THRM_TRIP_POINTS | THRM_MODE);
|
|||
|
|
|||
|
ACPIThermalStartDeviceExit:
|
|||
|
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
EXPORT
|
|||
|
ACPIThermalTempatureRead (
|
|||
|
IN PNSOBJ AcpiObject,
|
|||
|
IN NTSTATUS Status,
|
|||
|
IN POBJDATA Result OPTIONAL,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to read the temperature. It is used a callback to
|
|||
|
an interpreter call
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
AcpiObject - The object that was executed
|
|||
|
Status - The status of the execution
|
|||
|
Result - The result of the execution
|
|||
|
Context - The device extension
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PTHRM_INFO Thrm;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
|
|||
|
deviceExtension = Context;
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
ASSERT (Result->dwDataType == OBJTYPE_INTDATA);
|
|||
|
Thrm = deviceExtension->Thermal.Info;
|
|||
|
Thrm->Info.CurrentTemperature = (ULONG)Result->uipDataValue;
|
|||
|
AMLIFreeDataBuffs (Result, 1);
|
|||
|
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
deviceExtension,
|
|||
|
KeQueryInterruptTime(),
|
|||
|
"Current Temperature is %d.%dK\n",
|
|||
|
(Thrm->Info.CurrentTemperature / 10 ),
|
|||
|
(Thrm->Info.CurrentTemperature % 10 )
|
|||
|
) );
|
|||
|
|
|||
|
}
|
|||
|
ACPIThermalLoop (deviceExtension, THRM_BUSY);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ACPIThermalWorker (
|
|||
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|||
|
IN ULONG Events
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Worker thread for thermal regions
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceExtension - The device extension that we are manipulating
|
|||
|
Events - What just happened
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOLEAN TurnOn;
|
|||
|
PTHRM_INFO Thrm;
|
|||
|
NTSTATUS Status;
|
|||
|
PNSOBJ ThrmObj;
|
|||
|
PNSOBJ ALobj;
|
|||
|
OBJDATA ALPackage;
|
|||
|
OBJDATA ALElement;
|
|||
|
PNSOBJ ACDevObj;
|
|||
|
ULONG Index;
|
|||
|
ULONG Level;
|
|||
|
ULONG PackageSize;
|
|||
|
ULONGLONG currentTime;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
#if DBG
|
|||
|
currentTime = KeQueryInterruptTime();
|
|||
|
#endif
|
|||
|
|
|||
|
Thrm = DeviceExtension->Thermal.Info;
|
|||
|
ThrmObj = DeviceExtension->AcpiObject;
|
|||
|
|
|||
|
//
|
|||
|
// Initialization code
|
|||
|
//
|
|||
|
if (Events & THRM_INITIALIZE) {
|
|||
|
|
|||
|
ULONG names[10] = {
|
|||
|
PACKED_AL0,
|
|||
|
PACKED_AL1,
|
|||
|
PACKED_AL2,
|
|||
|
PACKED_AL3,
|
|||
|
PACKED_AL4,
|
|||
|
PACKED_AL5,
|
|||
|
PACKED_AL6,
|
|||
|
PACKED_AL7,
|
|||
|
PACKED_AL8,
|
|||
|
PACKED_AL9,
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Start the system in PASSIVE mode
|
|||
|
//
|
|||
|
Thrm->Mode = 1;
|
|||
|
|
|||
|
//
|
|||
|
// Fetch all of the objects associated with each cooling level
|
|||
|
//
|
|||
|
for (Level = 0; Level < 10; Level++) {
|
|||
|
|
|||
|
//
|
|||
|
// Find this level's active list
|
|||
|
//
|
|||
|
ALobj = ACPIAmliGetNamedChild(
|
|||
|
ThrmObj,
|
|||
|
names[Level]
|
|||
|
);
|
|||
|
if (ALobj == NULL) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember that we have this object
|
|||
|
//
|
|||
|
Thrm->ActiveList[Level] = ALobj;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do this before we update the trips points
|
|||
|
//
|
|||
|
if ( (Events & THRM_COOLING_LEVEL) ) {
|
|||
|
|
|||
|
RtlZeroMemory (&ALPackage, sizeof(OBJDATA));
|
|||
|
RtlZeroMemory (&ALElement, sizeof(OBJDATA));
|
|||
|
|
|||
|
for (Level=0; Level < 10; Level++) {
|
|||
|
|
|||
|
//
|
|||
|
// Is there a cooling object?
|
|||
|
//
|
|||
|
ALobj = Thrm->ActiveList[Level];
|
|||
|
if (ALobj == NULL) {
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Evalute the list to its package
|
|||
|
//
|
|||
|
Status = AMLIEvalNameSpaceObject(
|
|||
|
ALobj,
|
|||
|
&ALPackage,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember how large the package is
|
|||
|
//
|
|||
|
PackageSize = ((PPACKAGEOBJ) ALPackage.pbDataBuff)->dwcElements;
|
|||
|
|
|||
|
//
|
|||
|
// Walk the names in the package
|
|||
|
//
|
|||
|
for (Index = 0; Index < PackageSize; Index += 1) {
|
|||
|
|
|||
|
//
|
|||
|
// Grab the object name
|
|||
|
Status = AMLIEvalPkgDataElement(
|
|||
|
&ALPackage,
|
|||
|
Index,
|
|||
|
&ALElement
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine if we are going to the device on or off
|
|||
|
//
|
|||
|
TurnOn = (Level >= Thrm->CoolingLevel);
|
|||
|
|
|||
|
//
|
|||
|
// Tell the world
|
|||
|
//
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker: Turn %s %s\n",
|
|||
|
TurnOn ? "on " : "off",
|
|||
|
ALElement.pbDataBuff
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Find this device of this name
|
|||
|
//
|
|||
|
Status = AMLIGetNameSpaceObject(
|
|||
|
ALElement.pbDataBuff,
|
|||
|
ThrmObj,
|
|||
|
&ACDevObj,
|
|||
|
0
|
|||
|
);
|
|||
|
AMLIFreeDataBuffs (&ALElement, 1);
|
|||
|
if (!NT_SUCCESS(Status) || !ACDevObj->Context) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Turn it on/off
|
|||
|
//
|
|||
|
ACPIDeviceInternalDeviceRequest (
|
|||
|
(PDEVICE_EXTENSION) ACDevObj->Context,
|
|||
|
TurnOn ? PowerDeviceD0 : PowerDeviceD3,
|
|||
|
ACPIThermalPowerCallback,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
AMLIFreeDataBuffs (&ALPackage, 1);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the trip points need to be re-freshed, go read them
|
|||
|
//
|
|||
|
if (Events & THRM_TRIP_POINTS) {
|
|||
|
|
|||
|
ULONG names[10] = {
|
|||
|
PACKED_AC0,
|
|||
|
PACKED_AC1,
|
|||
|
PACKED_AC2,
|
|||
|
PACKED_AC3,
|
|||
|
PACKED_AC4,
|
|||
|
PACKED_AC5,
|
|||
|
PACKED_AC6,
|
|||
|
PACKED_AC7,
|
|||
|
PACKED_AC8,
|
|||
|
PACKED_AC9,
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Get the thermal constants, passive & critical values
|
|||
|
//
|
|||
|
ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_TC1,
|
|||
|
&Thrm->Info.ThermalConstant1,
|
|||
|
NULL
|
|||
|
);
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker - ThermalConstant1 = %x\n",
|
|||
|
Thrm->Info.ThermalConstant1
|
|||
|
) );
|
|||
|
#endif
|
|||
|
ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_TC2,
|
|||
|
&Thrm->Info.ThermalConstant2,
|
|||
|
NULL
|
|||
|
);
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker - ThermalConstant2 = %x\n",
|
|||
|
Thrm->Info.ThermalConstant2
|
|||
|
) );
|
|||
|
#endif
|
|||
|
ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_PSV,
|
|||
|
&Thrm->Info.PassiveTripPoint,
|
|||
|
NULL
|
|||
|
);
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker - PassiveTripPoint = %d.%dK\n",
|
|||
|
(Thrm->Info.PassiveTripPoint / 10),
|
|||
|
(Thrm->Info.PassiveTripPoint % 10)
|
|||
|
) );
|
|||
|
#endif
|
|||
|
ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_CRT,
|
|||
|
&Thrm->Info.CriticalTripPoint,
|
|||
|
NULL
|
|||
|
);
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker - CriticalTripPoint = %d.%dK\n",
|
|||
|
(Thrm->Info.CriticalTripPoint / 10),
|
|||
|
(Thrm->Info.CriticalTripPoint % 10)
|
|||
|
) );
|
|||
|
#endif
|
|||
|
ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
PACKED_TSP,
|
|||
|
&Thrm->Info.SamplingPeriod,
|
|||
|
NULL
|
|||
|
);
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker - SamplingPeriod = %x\n",
|
|||
|
Thrm->Info.SamplingPeriod
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Get the active cooling limits
|
|||
|
//
|
|||
|
for (Level=0; Level < 10; Level++) {
|
|||
|
|
|||
|
Status = ACPIGetIntegerSync(
|
|||
|
DeviceExtension,
|
|||
|
names[Level],
|
|||
|
&Thrm->Info.ActiveTripPoint[Level],
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
#if DBG
|
|||
|
ACPIThermalPrint( (
|
|||
|
ACPI_PRINT_THERMAL,
|
|||
|
DeviceExtension,
|
|||
|
currentTime,
|
|||
|
"ACPIThermalWorker - Active Cooling Level %x = %d.%dK\n",
|
|||
|
Level,
|
|||
|
(Thrm->Info.ActiveTripPoint[Level] / 10),
|
|||
|
(Thrm->Info.ActiveTripPoint[Level] % 10)
|
|||
|
) );
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
Thrm->Info.ActiveTripPointCount = (UCHAR) Level;
|
|||
|
|
|||
|
//
|
|||
|
// Clean these variables for reuse
|
|||
|
//
|
|||
|
RtlZeroMemory (&ALPackage, sizeof(OBJDATA));
|
|||
|
RtlZeroMemory (&ALElement, sizeof(OBJDATA));
|
|||
|
|
|||
|
//
|
|||
|
// Assume an affinity of 0
|
|||
|
//
|
|||
|
Thrm->Info.Processors = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Get the passive cooling affinity object
|
|||
|
//
|
|||
|
ALobj = ACPIAmliGetNamedChild(
|
|||
|
ThrmObj,
|
|||
|
PACKED_PSL
|
|||
|
);
|
|||
|
if (ALobj != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Evaluate the processor affinity object
|
|||
|
//
|
|||
|
Status = AMLIEvalNameSpaceObject(
|
|||
|
ALobj,
|
|||
|
&ALPackage,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
goto ACPIThermalWorkerExit;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remember how large the package is
|
|||
|
//
|
|||
|
PackageSize = ((PPACKAGEOBJ) ALPackage.pbDataBuff)->dwcElements;
|
|||
|
|
|||
|
//
|
|||
|
// Walk the elements in the package
|
|||
|
//
|
|||
|
for (Index = 0; Index < PackageSize ;Index++) {
|
|||
|
|
|||
|
Status = AMLIEvalPkgDataElement(
|
|||
|
&ALPackage,
|
|||
|
Index,
|
|||
|
&ALElement
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find this device of this name
|
|||
|
//
|
|||
|
Status = AMLIGetNameSpaceObject(
|
|||
|
ALElement.pbDataBuff,
|
|||
|
NULL,
|
|||
|
&ACDevObj,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// No longer need this information
|
|||
|
//
|
|||
|
AMLIFreeDataBuffs (&ALElement, 1);
|
|||
|
|
|||
|
//
|
|||
|
// Did we find what we wanted?
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(Status) ) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the correct affinity mask. We call another
|
|||
|
// function since that one requires a spinlock which
|
|||
|
// don't want to take in this worker function
|
|||
|
//
|
|||
|
ACPIThermalCalculateProcessorMask( ACDevObj, Thrm );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We are done with the package
|
|||
|
//
|
|||
|
AMLIFreeDataBuffs (&ALPackage, 1);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ACPIThermalWorkerExit:
|
|||
|
|
|||
|
//
|
|||
|
// done, check for next work
|
|||
|
//
|
|||
|
ACPIThermalLoop (DeviceExtension, THRM_TEMP | THRM_BUSY);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ACPIThermalWmi(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PWMILIB_CONTEXT wmilibContext;
|
|||
|
SYSCTL_IRP_DISPOSITION disposition;
|
|||
|
|
|||
|
wmilibContext = deviceExtension->Thermal.WmilibContext;
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
status = WmiSystemControl(
|
|||
|
wmilibContext,
|
|||
|
DeviceObject,
|
|||
|
Irp,
|
|||
|
&disposition
|
|||
|
);
|
|||
|
|
|||
|
switch (disposition) {
|
|||
|
|
|||
|
case IrpProcessed:
|
|||
|
break;
|
|||
|
case IrpNotCompleted:
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
break;
|
|||
|
case IrpNotWmi:
|
|||
|
case IrpForward:
|
|||
|
default:
|
|||
|
status = ACPIDispatchForwardIrp(DeviceObject, Irp);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|