/*++ 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 |= ( (ULONG_PTR) 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. The ACPIThermalLoop routine may process this request right // away or do it later depending on wether it is busy doing some work when // it is called. Therefore we should mark this IRP pending. There is no harm // in marking it pending and returning STATUS_PENDING even if the work // gets completed by ACPIThermalLoop syncronously. // 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; }