/*++ Copyright (c) 1990 Microsoft Corporation Module Name: idle.c Abstract: This module implements the power management idle timing code for device objects Author: Bryan Willman (bryanwi) 7-Nov-96 Revision History: --*/ #include "pop.h" NTKERNELAPI PULONG PoRegisterDeviceForIdleDetection ( IN PDEVICE_OBJECT DeviceObject, IN ULONG ConservationIdleTime, IN ULONG PerformanceIdleTime, IN DEVICE_POWER_STATE State ) /*++ Routine Description: A device driver calls this routine to either: a. Create and initialize a new idle detection block b. Reset values in an existing idle detection block If the device object has an idle detection block, it is filled in with new values. Otherwise, an idle detect block is created and linked to the device object. Arguments: DeviceObject - Device object which wants idle detection, set_power IRPs will be sent here ConservationIdleTime - timeout for system in "conserve mode" PerformanceIdleTime - timeout for system in "performance mode" Type - Type of set_power sent (for set_power irp) State - what state to go to (for set_power irp) Return Value: NULL - if an attempt to create a new idle block failed non-NULL - if an idle block was created, or if an existing one was reset --*/ { PDEVICE_OBJECT_POWER_EXTENSION pdope; KIRQL OldIrql; NTSTATUS Status; ULONG DeviceType, OldDeviceType; ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); // // deal with the case where idle detection is being turned off // if ((ConservationIdleTime == 0) && (PerformanceIdleTime == 0)) { PopLockDopeGlobal(&OldIrql); pdope = DeviceObject->DeviceObjectExtension->Dope; if (pdope == NULL) { // // cannot be linked into the chain, so must already be off, // so we're done // } else { // // there is a pdope, so we may be on the idle list // if ((pdope->IdleList.Flink == &(pdope->IdleList)) && (pdope->IdleList.Blink == &(pdope->IdleList))) { // // we're off the queue already, so we're done // } else { // // a dope vector exists and is on the idle scan list, // so we must delist ourselves // RemoveEntryList(&(pdope->IdleList)); OldDeviceType = pdope->DeviceType | ES_CONTINUOUS; pdope->DeviceType = 0; PopApplyAttributeState (ES_CONTINUOUS, OldDeviceType); pdope->ConservationIdleTime = 0L; pdope->PerformanceIdleTime = 0L; pdope->State = PowerDeviceUnspecified; pdope->IdleCount = 0; InitializeListHead(&(pdope->IdleList)); } } PopUnlockDopeGlobal(OldIrql); return NULL; } // // Set DeviceType if this is an idle registration by type // DeviceType = 0; if (ConservationIdleTime == (ULONG) -1 && PerformanceIdleTime == (ULONG) -1) { switch (DeviceObject->DeviceType) { case FILE_DEVICE_DISK: case FILE_DEVICE_MASS_STORAGE: DeviceType = POP_DISK_SPINDOWN | ES_CONTINUOUS; break; default: // // Unsupported type // return NULL; } } // // now, the case where it's being turned on // pdope = PopGetDope(DeviceObject); if (pdope == NULL) { // // we didn't have a DOPE structure and couldn't allocate one, fail // return (PVOID)NULL; } // // May be a newly allocated Dope, or an existing one. // In either case, update values. // Enqueue if not already in queue // PopLockDopeGlobal(&OldIrql); OldDeviceType = pdope->DeviceType | ES_CONTINUOUS; pdope->ConservationIdleTime = ConservationIdleTime; pdope->PerformanceIdleTime = PerformanceIdleTime; pdope->State = State; pdope->IdleCount = 0; pdope->DeviceType = (UCHAR) DeviceType; if ((pdope->IdleList.Flink == &(pdope->IdleList)) && (pdope->IdleList.Blink == &(pdope->IdleList))) { // // we're off the queue, and must be enqueued // InsertTailList(&PopIdleDetectList, &(pdope->IdleList)); } PopUnlockDopeGlobal(OldIrql); PopApplyAttributeState(DeviceType, OldDeviceType); PopCheckForWork(TRUE); return &(pdope->IdleCount); // success } VOID PopScanIdleList( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: Called by the PopIdleScanTimer at PopIdleScanTimeInseconds interval, this routine runs the list of Idle Blocks, finding any that meet the trip conditions, and sends commands to the appropriate device objects to change state. The timer that calls this DPC is setup in poinit.c. Arguments: Standard DPC arguments, all are ignored. Return Value: None. --*/ { KIRQL OldIrql; PLIST_ENTRY link; ULONG idlelimit; PDEVICE_OBJECT_POWER_EXTENSION pblock; POWER_STATE PowerState; PULONG pIdleCount; ULONG oldCount; PopLockDopeGlobal(&OldIrql); link = PopIdleDetectList.Flink; while (link != &PopIdleDetectList) { pblock = CONTAINING_RECORD(link, DEVICE_OBJECT_POWER_EXTENSION, IdleList); pIdleCount = &(pblock->IdleCount); oldCount = InterlockedIncrement(pIdleCount); switch (pblock->DeviceType) { case 0: idlelimit = pblock->PerformanceIdleTime; if (PopIdleDetectionMode == PO_IDLE_CONSERVATION) { idlelimit = pblock->ConservationIdleTime; } break; case POP_DISK_SPINDOWN: idlelimit = PopPolicy->SpindownTimeout; break; default: PopInternalAddToDumpFile( NULL, 0, pblock->DeviceObject, NULL, NULL, pblock ); KeBugCheckEx( INTERNAL_POWER_ERROR, 0x200, POP_IDLE, (ULONG_PTR)pblock->DeviceObject, (ULONG_PTR)pblock ); } if ((idlelimit > 0) && ((oldCount+1) == idlelimit)) { PowerState.DeviceState = pblock->State; PoRequestPowerIrp ( pblock->DeviceObject, IRP_MN_SET_POWER, PowerState, NULL, NULL, NULL ); } link = link->Flink; } PopUnlockDopeGlobal(OldIrql); return; } PDEVICE_OBJECT_POWER_EXTENSION PopGetDope ( PDEVICE_OBJECT DeviceObject ) { PDEVOBJ_EXTENSION Doe; PDEVICE_OBJECT_POWER_EXTENSION Dope; KIRQL OldIrql; Doe = (PDEVOBJ_EXTENSION) DeviceObject->DeviceObjectExtension; if (!Doe->Dope) { PopLockDopeGlobal(&OldIrql); if (!Doe->Dope) { Dope = (PDEVICE_OBJECT_POWER_EXTENSION) ExAllocatePoolWithTag( NonPagedPool, sizeof(DEVICE_OBJECT_POWER_EXTENSION), POP_DOPE_TAG ); if (Dope) { RtlZeroMemory (Dope, sizeof(DEVICE_OBJECT_POWER_EXTENSION)); Dope->DeviceObject = DeviceObject; Dope->State = PowerDeviceUnspecified; InitializeListHead(&(Dope->IdleList)); InitializeListHead(&(Dope->NotifySourceList)); InitializeListHead(&(Dope->NotifyTargetList)); // force the signature to 0 so buildpowerchannel gets called Dope->PowerChannelSummary.Signature = (ULONG)0; InitializeListHead(&(Dope->PowerChannelSummary.NotifyList)); Doe->Dope = Dope; } } PopUnlockDopeGlobal(OldIrql); } return Doe->Dope; }