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

1809 lines
44 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 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;
}